Pārlūkot izejas kodu

CI: Update build scripts and Github actions workflow

Also updates main CMakeLists.txt and CMakeLists.txt for unit tests,
also adds additional build directories to .gitignore file
PatTheMav 3 gadi atpakaļ
vecāks
revīzija
c993b6059a
63 mainītis faili ar 3377 papildinājumiem un 2041 dzēšanām
  1. 14 0
      .cmake-format.json
  2. 9 3
      .github/workflows/clang-format.yml
  3. 0 40
      .github/workflows/flatpak.yml
  4. 350 591
      .github/workflows/main.yml
  5. 3 2
      .gitignore
  6. 0 3
      CI/before-deploy-win.cmd
  7. 0 6
      CI/before-script-linux.sh
  8. 98 0
      CI/build-freebsd.sh
  9. 105 0
      CI/build-linux.sh
  10. 155 0
      CI/build-macos.sh
  11. 137 0
      CI/build-windows.ps1
  12. 11 0
      CI/check-changes.sh
  13. 51 0
      CI/check-cmake.sh
  14. 59 10
      CI/check-format.sh
  15. 5 4
      CI/flatpak/com.obsproject.Studio.json
  16. 100 0
      CI/freebsd/01_install_dependencies.sh
  17. 120 0
      CI/freebsd/02_build_obs.sh
  18. 78 0
      CI/freebsd/03_package_obs.sh
  19. 0 726
      CI/full-build-macos.sh
  20. 5 0
      CI/include/Brewfile
  21. 2 0
      CI/include/Wingetfile
  22. 217 0
      CI/include/build_support.sh
  23. 42 0
      CI/include/build_support_freebsd.sh
  24. 44 0
      CI/include/build_support_linux.sh
  25. 156 0
      CI/include/build_support_macos.sh
  26. 156 0
      CI/include/build_support_windows.ps1
  27. 0 52
      CI/install-dependencies-linux.sh
  28. 0 4
      CI/install-qt-win.cmd
  29. 0 4
      CI/install-script-linux.sh
  30. 0 30
      CI/install-script-win.cmd
  31. 136 0
      CI/linux/01_install_dependencies.sh
  32. 127 0
      CI/linux/02_build_obs.sh
  33. 82 0
      CI/linux/03_package_obs.sh
  34. 178 0
      CI/macos/01_install_dependencies.sh
  35. 161 0
      CI/macos/02_build_obs.sh
  36. 185 0
      CI/macos/03_package_obs.sh
  37. 0 5
      CI/scripts/macos/Brewfile
  38. BIN
      CI/scripts/macos/app/AppIcon.icns
  39. 0 36
      CI/scripts/macos/app/OBSPublicDSAKey.pem
  40. BIN
      CI/scripts/macos/app/big_sur_icon_template.psd
  41. BIN
      CI/scripts/macos/app/dylibbundler
  42. 0 17
      CI/scripts/macos/app/entitlements.plist
  43. BIN
      CI/scripts/macos/app/obs.icns
  44. 0 12
      CI/scripts/macos/helpers/helper-entitlements.plist
  45. 0 8
      CI/scripts/macos/helpers/helper-gpu-entitlements.plist
  46. 0 10
      CI/scripts/macos/helpers/helper-plugin-entitlements.plist
  47. 0 8
      CI/scripts/macos/helpers/helper-renderer-entitlements.plist
  48. BIN
      CI/scripts/macos/package/background.tiff
  49. 0 28
      CI/scripts/macos/package/settings.json.template
  50. BIN
      CI/scripts/macos/package/src/background.png
  51. BIN
      CI/scripts/macos/package/src/[email protected]
  52. 0 1
      CI/scripts/macos/package/src/makeRetinaBG
  53. 177 0
      CI/windows/01_install_dependencies.ps1
  54. 116 0
      CI/windows/02_build_obs.ps1
  55. 155 0
      CI/windows/03_package_obs.ps1
  56. 72 289
      CMakeLists.txt
  57. 23 25
      cmake/Modules/CompilerConfig.cmake
  58. 0 43
      formatcode.sh
  59. 9 10
      test/CMakeLists.txt
  60. 6 33
      test/cmocka/CMakeLists.txt
  61. 12 13
      test/osx/CMakeLists.txt
  62. 16 21
      test/test-input/CMakeLists.txt
  63. 5 7
      test/win/CMakeLists.txt

+ 14 - 0
.cmake-format.json

@@ -0,0 +1,14 @@
+{
+    "additional_commands": {
+      "find_qt": {
+        "flags": [],
+        "kwargs": {
+          "VERSION": "+",
+          "COMPONENTS": "+",
+          "COMPONENTS_WIN": "+",
+          "COMPONENTS_MACOS": "+",
+          "COMPONENTS_LINUX": "+"
+        }
+      }
+    }
+}

+ 9 - 3
.github/workflows/clang-format.yml

@@ -1,6 +1,12 @@
 name: Clang Format Check
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore: ['**.md']
+    branches-ignore: [master]
+  pull_request:
+    paths-ignore: ['**.md']
+    branches-ignore: [master]
 
 jobs:
   clang-format-check:
@@ -15,7 +21,7 @@ jobs:
         run: |
           sudo apt-get install -y clang-format-12
 
-      - name: Check the Formatting
+      - name: 'Run clang-format'
         run: |
-          ./formatcode.sh
           ./CI/check-format.sh
+          ./CI/check-changes.sh

+ 0 - 40
.github/workflows/flatpak.yml

@@ -3,12 +3,6 @@
 name: Flatpak
 
 on:
-  push:
-    paths-ignore: ['**.md']
-    branches: [master, 'release/**']
-  pull_request:
-    paths-ignore: ['**.md']
-    branches: [master, 'release/**']
   release:
     types: [published]
     branches: [master, 'release/**']
@@ -24,40 +18,6 @@ env:
   YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}
 
 jobs:
-  generate_bundle:
-    name: Generate Flatpak Bundle
-    runs-on: [ubuntu-latest]
-    if: github.event_name != 'release'
-    container:
-      image: bilelmoussaoui/flatpak-github-actions:kde-5.15-21.08
-      options: --privileged
-    steps:
-      - name: 'Check for Github Labels'
-        if: github.event_name == 'pull_request'
-        shell: bash
-        run: |
-          LABELS_URL="$(echo ${{ github.event.pull_request.url }} | sed s'/pulls/issues/')"
-          LABEL_FOUND="$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "${LABELS_URL}/labels" | sed -n 's/.*"name": "\(.*\)",/\1/p' | grep 'Seeking Testers' || true)"
-          if [ "${LABEL_FOUND}" = "Seeking Testers" ]; then
-            echo "SEEKING_TESTERS=1" >> $GITHUB_ENV
-          else
-            echo "SEEKING_TESTERS=0" >> $GITHUB_ENV
-          fi
-
-      - name: Checkout
-        uses: actions/[email protected]
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        with:
-          submodules: 'recursive'
-
-      - name: Build Flatpak Manifest
-        uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v4
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        with:
-          bundle: obs-studio-${{ github.sha }}.flatpak
-          manifest-path: CI/flatpak/com.obsproject.Studio.json
-          cache-key: flatpak-builder-${{ github.sha }}
-
   publish:
     name: Publish to Flathub
     runs-on: [ubuntu-latest]

+ 350 - 591
.github/workflows/main.yml

@@ -1,24 +1,26 @@
-name: 'CI Multiplatform Build'
+name: 'BUILD'
 
 on:
   push:
-    paths-ignore:
-      - '**.md'
+    paths-ignore: ['**.md']
     branches:
       - master
       - 'release/**'
-    tags:
-      - '*'
+    tags: ['*']
   pull_request:
-    paths-ignore:
-      - '**.md'
-    branches:
-      - master
+    paths-ignore: ['**.md']
+    branches: [master]
 
 env:
-  MACOS_CEF_BUILD_VERSION: '4638'
-  LINUX_CEF_BUILD_VERSION: '4638'
-  WINDOWS_CEF_BUILD_VERSION: '4638'
+  CEF_BUILD_VERSION_MAC: '4638'
+  CEF_BUILD_VERSION_LINUX: '4638'
+  CEF_BUILD_VERSION_WIN: '4638'
+  QT_VERSION_MAC: '5.15.2'
+  QT_VERSION_WIN: '5.15.2'
+  DEPS_VERSION_MAC: '2022-02-13'
+  DEPS_VERSION_WIN: '2022-01-31'
+  VLC_VERSION_MAC: '3.0.8'
+  VLC_VERSION_WIN: '3.0.0-git'
   TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}
   TWITCH_HASH: ${{ secrets.TWITCH_HASH }}
   RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}
@@ -29,687 +31,444 @@ env:
   YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}
 
 jobs:
-  macos64:
-    name: 'macOS 64-bit'
-    runs-on: [macos-latest]
+  clang_check:
+    name: '01 - Code Format Check'
+    runs-on: [ubuntu-latest]
+    steps:
+      - name: 'Checkout'
+        uses: actions/[email protected]
+        with:
+          submodules: 'recursive'
+
+      - name: 'Install clang-format'
+        run: sudo apt-get install -y clang-format-12
+
+      - name: 'Run clang-format'
+        run: |
+          ./CI/check-format.sh
+          ./CI/check-changes.sh
+
+      - name: 'Install cmake-format'
+        run: sudo pip install cmakelang
+
+      - name: 'Run cmake-format'
+        run: |
+          ./CI/check-cmake.sh
+
+  macos_build:
+    name: '02 - macOS'
+    runs-on: [macos-11]
+    strategy:
+      matrix:
+        arch: ['x86_64']
+    if: always()
+    needs: [clang_check]
     env:
-      MIN_MACOS_VERSION: '10.13'
-      MACOS_DEPS_VERSION: '2022-02-13'
-      VLC_VERSION: '3.0.8'
-      SPARKLE_VERSION: '1.23.0'
-      QT_VERSION: '5.15.2'
-      SIGN_IDENTITY: ''
+      MACOSX_DEPLOYMENT_TARGET: '10.13'
+      SPARKLE_VERSION: '1.26.0'
+      SPARKLE_HASH: '8312cbf7528297a49f1b97692c33cb8d33254c396dc51be394e9484e4b6833a0'
+      BLOCKED_FORMULAS: 'speexdsp curl php composer'
+      CODESIGN_IDENT: '-'
       HAVE_CODESIGN_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY != '' && secrets.MACOS_SIGNING_CERT != '' }}
-    steps:
-      - name: Get Current Arch
+    defaults:
+      run:
         shell: bash
-        id: get_arch
-        run: echo "CURRENT_ARCH=$(uname -m)" >> $GITHUB_ENV
+        working-directory: 'obs-studio'
+    steps:
       - name: 'Checkout'
         uses: actions/[email protected]
         with:
           submodules: 'recursive'
-      - name: 'Fetch Git Tags'
-        run: |
-          git fetch --prune --unshallow
-          echo "OBS_GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_ENV
-          echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
-          echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
+          path: 'obs-studio'
+          fetch-depth: 0
+
       - name: 'Check for Github Labels'
         if: github.event_name == 'pull_request'
         run: |
-          LABELS_URL="$(echo ${{ github.event.pull_request.url }} | sed s'/pulls/issues/')"
-          LABEL_FOUND="$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "${LABELS_URL}/labels" | sed -n 's/.*"name": "\(.*\)",/\1/p' | grep 'Seeking Testers' || true)"
-          if [ "${LABEL_FOUND}" = "Seeking Testers" ]; then
+          if test -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')"; then
             echo "SEEKING_TESTERS=1" >> $GITHUB_ENV
           else
             echo "SEEKING_TESTERS=0" >> $GITHUB_ENV
           fi
-      - name: 'Setup build environment (Homebrew + ENV)'
-        shell: bash
-        run: |
-          if [ -d /usr/local/opt/[email protected] ]; then
-            brew uninstall [email protected]
-            brew untap local/openssl
-          fi
 
-          if [ -d /usr/local/opt/[email protected] ]; then
-            brew uninstall [email protected]
-            brew untap local/python2
-          fi
+          echo "CACHE_DATE=$(date +"%Y-%m-%d")" >> $GITHUB_ENV
+
+      - name: 'Restore ccache from cache'
+        id: ccache-cache
+        uses: actions/[email protected]
+        env:
+          CACHE_NAME: 'ccache-cache'
+        with:
+          path: ${{ github.workspace }}/.ccache
+          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ matrix.arch }}-${{ env.CACHE_DATE }}
 
-          if [ -d /usr/local/opt/speexdsp ]; then
-            brew unlink speexdsp
-          fi
-          brew uninstall curl php composer
-          brew bundle --file ./CI/scripts/macos/Brewfile
-          echo "NPROC=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV
       - name: 'Restore Chromium Embedded Framework from cache'
         id: cef-cache
         uses: actions/[email protected]
         env:
           CACHE_NAME: 'cef-cache'
         with:
-          path: ${{ github.workspace }}/cmbuild/cef_binary_${{ env.MACOS_CEF_BUILD_VERSION }}_macos_x86_64
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.MACOS_CEF_BUILD_VERSION }}-1
+          path: ${{ github.workspace }}/obs-build-dependencies/cef_binary_${{ env.CEF_BUILD_VERSION_MAC }}_macos_${{ matrix.arch }}
+          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.CEF_BUILD_VERSION_MAC }}-${{ matrix.arch }}
+
       - name: 'Restore VLC dependency from cache'
         id: vlc-cache
         uses: actions/[email protected]
         env:
           CACHE_NAME: 'vlc-cache'
         with:
-          path: ${{ github.workspace }}/cmbuild/vlc-${{ env.VLC_VERSION }}
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.VLC_VERSION }}
+          path: ${{ github.workspace }}/obs-build-dependencies/vlc-${{ env.VLC_VERSION_MAC }}
+          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.VLC_VERSION_MAC }}
+
       - name: 'Restore Sparkle dependency from cache'
         id: sparkle-cache
         uses: actions/[email protected]
         env:
           CACHE_NAME: 'sparkle-cache'
         with:
-          path: ${{ github.workspace }}/cmbuild/sparkle
+          path: ${{ github.workspace }}/obs-build-dependencies/sparkle
           key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.SPARKLE_VERSION }}
-      - name: 'Install prerequisite: Pre-built dependencies'
-        if: steps.deps-cache.outputs.cache-hit != 'true'
-        shell: bash
-        run: |
-          mkdir /tmp/obsdeps
-          curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-deps-${{ env.MACOS_DEPS_VERSION }}-${{ env.CURRENT_ARCH }}.tar.xz
-          tar -xf ./macos-deps-${{ env.MACOS_DEPS_VERSION }}-${{ env.CURRENT_ARCH }}.tar.xz -C "/tmp/obsdeps"
-      - name: 'Install prerequisite: Pre-built dependency Qt'
-        if: steps.deps-qt-cache.outputs.cache-hit != 'true'
-        shell: bash
-        run: |
-          curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-deps-qt-${{ env.MACOS_DEPS_VERSION }}-${{ env.CURRENT_ARCH }}.tar.xz
-          tar -xf ./macos-deps-qt-${{ env.MACOS_DEPS_VERSION }}-${{ env.CURRENT_ARCH }}.tar.xz -C "/tmp/obsdeps"
-          xattr -r -d com.apple.quarantine /tmp/obsdeps
-      - name: 'Install prerequisite: VLC'
-        if: steps.vlc-cache.outputs.cache-hit != 'true'
-        shell: bash
-        run: |
-          curl -L -O https://downloads.videolan.org/vlc/${{ env.VLC_VERSION }}/vlc-${{ env.VLC_VERSION }}.tar.xz
-          if [ ! -d "${{ github.workspace }}/cmbuild" ]; then mkdir "${{ github.workspace }}/cmbuild"; fi
-          tar -xf ./vlc-${{ env.VLC_VERSION }}.tar.xz -C "${{ github.workspace }}/cmbuild"
-      - name: 'Install prerequisite: Sparkle'
-        if: steps.sparkle-cache.outputs.cache-hit != 'true'
-        shell: bash
-        run: |
-          curl -L -o sparkle.tar.bz2 https://github.com/sparkle-project/Sparkle/releases/download/${{ env.SPARKLE_VERSION }}/Sparkle-${{ env.SPARKLE_VERSION }}.tar.bz2
-          mkdir ${{ github.workspace }}/cmbuild/sparkle
-          tar -xf ./sparkle.tar.bz2 -C ${{ github.workspace }}/cmbuild/sparkle
-      - name: 'Setup prerequisite: Sparkle'
-        shell: bash
-        run: sudo cp -R ${{ github.workspace }}/cmbuild/sparkle/Sparkle.framework /Library/Frameworks/Sparkle.framework
-      - name: 'Install prerequisite: Chromium Embedded Framework'
-        if: steps.cef-cache.outputs.cache-hit != 'true'
-        shell: bash
-        run: |
-          curl -L -O https://cdn-fastly.obsproject.com/downloads/cef_binary_${{ env.MACOS_CEF_BUILD_VERSION }}_macos_x86_64.tar.xz
-          tar -xf ./cef_binary_${{ env.MACOS_CEF_BUILD_VERSION }}_macos_x86_64.tar.xz -C ${{ github.workspace }}/cmbuild/
-          cd ${{ github.workspace }}/cmbuild/cef_binary_${{ env.MACOS_CEF_BUILD_VERSION }}_macos_x86_64
-          /usr/bin/sed -i '.orig' '/add_subdirectory(tests\/ceftests)/d' ./CMakeLists.txt
-          /usr/bin/sed -i '.orig' s/\"10.9\"/\"${{ env.MIN_MACOS_VERSION }}\"/ ./cmake/cef_variables.cmake
-          mkdir build && cd build
-          cmake  -DCMAKE_CXX_FLAGS="-std=c++11 -stdlib=libc++ -Wno-deprecated-declarations" -DCMAKE_EXE_LINKER_FLAGS="-std=c++11 -stdlib=libc++" -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ env.MIN_MACOS_VERSION }}  ..
-          make -j${NPROC:-4}
-          mkdir libcef_dll
-          cd ../../
-      - name: 'Configure'
-        shell: bash
-        run: |
-          mkdir ./build
-          cd ./build
-          LEGACY_BROWSER="$(test "${{ env.MACOS_CEF_BUILD_VERSION }}" -le 3770 && echo "ON" || echo "OFF")"
-          cmake -DENABLE_UNIT_TESTS=YES -DENABLE_SPARKLE_UPDATER=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ env.MIN_MACOS_VERSION }} -DQTDIR="/tmp/obsdeps" -DSWIGDIR="/tmp/obsdeps" -DDepsPath="/tmp/obsdeps" -DVLCPath="${{ github.workspace }}/cmbuild/vlc-${{ env.VLC_VERSION }}" -DENABLE_VLC=ON -DBUILD_BROWSER=ON -DBROWSER_LEGACY=$LEGACY_BROWSER -DWITH_RTMPS=ON -DCEF_ROOT_DIR="${{ github.workspace }}/cmbuild/cef_binary_${{ env.MACOS_CEF_BUILD_VERSION }}_macos_x86_64" -DTWITCH_CLIENTID='${{ env.TWITCH_CLIENTID }}' -DTWITCH_HASH='${{ env.TWITCH_HASH }}' -DRESTREAM_CLIENTID='${{ env.RESTREAM_CLIENTID }}' -DRESTREAM_HASH='${{ env.RESTREAM_HASH }}' ..
-      - name: 'Build'
-        shell: bash
-        working-directory: ${{ github.workspace }}/build
-        run: make -j${NPROC:-4}
-      - name: 'Test'
-        shell: bash
-        working-directory: ${{ github.workspace }}/build
-        run: make CTEST_OUTPUT_ON_FAILURE=1 test
-      - name: 'Install prerequisite: DMGbuild'
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        shell: bash
-        run: |
-          pip3 install dmgbuild==1.5.2
+
+      - name: 'Setup build environment'
+        run: |
+          REMOVE_FORMULAS=""
+          for FORMULA in ${{ env.BLOCKED_FORMULAS }}; do
+            if [ -d "/usr/local/opt/${FORMULA}" ]; then
+              REMOVE_FORMULAS="${REMOVE_FORMULAS}${FORMULA} "
+            fi
+          done
+
+          if [ -n "${REMOVE_FORMULAS}" ]; then
+            brew uninstall ${REMOVE_FORMULAS}
+          fi
+
+      - name: 'Install dependencies'
+        env:
+          RESTORED_VLC: ${{ steps.vlc-cache.outputs.cache-hit }}
+          RESTORED_SPARKLE: ${{ steps.sparkle-cache.outputs.cache-hit }}
+          RESTORED_CEF: ${{ steps.cef-cache.outputs.cache-hit }}
+        run: CI/macos/01_install_dependencies.sh --architecture "${{ matrix.arch }}"
+
       - name: 'Install Apple Developer Certificate'
-        if: success() && startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' && env.HAVE_CODESIGN_IDENTITY == 'true'
+        if: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' && env.HAVE_CODESIGN_IDENTITY == 'true' }}
         uses: apple-actions/import-codesign-certs@253ddeeac23f2bdad1646faac5c8c2832e800071
         with:
           p12-file-base64: ${{ secrets.MACOS_SIGNING_CERT }}
           p12-password: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }}
+
       - name: 'Set Signing Identity'
-        if: success() && startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' && env.HAVE_CODESIGN_IDENTITY == 'true'
+        if: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' && env.HAVE_CODESIGN_IDENTITY == 'true' }}
         run: |
-          echo "SIGN_IDENTITY=${{ secrets.MACOS_SIGNING_IDENTITY }}" >> $GITHUB_ENV
-      - name: 'Create macOS application bundle'
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        working-directory: ${{ github.workspace }}/build
-        shell: bash
-        run: |
-          if [ -d ./OBS.app ]; then rm -rf ./OBS.app; fi
-          mkdir -p OBS.app/Contents/MacOS
-          mkdir OBS.app/Contents/PlugIns
-          mkdir OBS.app/Contents/Resources
-          mkdir OBS.app/Contents/Frameworks
-
-          cp rundir/RelWithDebInfo/bin/obs ./OBS.app/Contents/MacOS
-          cp rundir/RelWithDebInfo/bin/obs-ffmpeg-mux ./OBS.app/Contents/MacOS
-          if ! [ "${{ env.MACOS_CEF_BUILD_VERSION }}" -le 3770 ]; then
-            cp -R "rundir/RelWithDebInfo/bin/OBS Helper.app" "./OBS.app/Contents/Frameworks/OBS Helper.app"
-            cp -R "rundir/RelWithDebInfo/bin/OBS Helper (GPU).app" "./OBS.app/Contents/Frameworks/OBS Helper (GPU).app"
-            cp -R "rundir/RelWithDebInfo/bin/OBS Helper (Plugin).app" "./OBS.app/Contents/Frameworks/OBS Helper (Plugin).app"
-            cp -R "rundir/RelWithDebInfo/bin/OBS Helper (Renderer).app" "./OBS.app/Contents/Frameworks/OBS Helper (Renderer).app"
-          fi
-          cp rundir/RelWithDebInfo/bin/libobsglad.0.dylib ./OBS.app/Contents/MacOS
-          cp -R rundir/RelWithDebInfo/data ./OBS.app/Contents/Resources
-          cp ../CI/scripts/macos/app/AppIcon.icns ./OBS.app/Contents/Resources
-          cp -R rundir/RelWithDebInfo/obs-plugins/ ./OBS.app/Contents/PlugIns
-          cp ../CI/scripts/macos/app/Info.plist ./OBS.app/Contents
-
-          if [ -d ./OBS.app/Contents/Resources/data/obs-scripting ]; then
-            mv ./OBS.app/Contents/Resources/data/obs-scripting/obslua.so ./OBS.app/Contents/MacOS/
-            mv ./OBS.app/Contents/Resources/data/obs-scripting/_obspython.so ./OBS.app/Contents/MacOS/
-            mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/MacOS/
-            rm -rf ./OBS.app/Contents/Resources/data/obs-scripting/
-          fi
-
-          /bin/cp -cpR /tmp/obsdeps/lib/*.dylib ./OBS.app/Contents/Frameworks
-
-          BUNDLE_PLUGINS=(
-            ./OBS.app/Contents/PlugIns/coreaudio-encoder.so
-            ./OBS.app/Contents/PlugIns/decklink-ouput-ui.so
-            ./OBS.app/Contents/PlugIns/decklink-captions.so
-            ./OBS.app/Contents/PlugIns/frontend-tools.so
-            ./OBS.app/Contents/PlugIns/image-source.so
-            ./OBS.app/Contents/PlugIns/mac-avcapture.so
-            ./OBS.app/Contents/PlugIns/mac-capture.so
-            ./OBS.app/Contents/PlugIns/mac-decklink.so
-            ./OBS.app/Contents/PlugIns/mac-syphon.so
-            ./OBS.app/Contents/PlugIns/mac-vth264.so
-            ./OBS.app/Contents/PlugIns/mac-virtualcam.so
-            ./OBS.app/Contents/PlugIns/obs-browser.so
-            ./OBS.app/Contents/PlugIns/obs-ffmpeg.so
-            ./OBS.app/Contents/PlugIns/obs-filters.so
-            ./OBS.app/Contents/PlugIns/obs-transitions.so
-            ./OBS.app/Contents/PlugIns/obs-vst.so
-            ./OBS.app/Contents/PlugIns/rtmp-services.so
-            ./OBS.app/Contents/MacOS/obs-ffmpeg-mux
-            ./OBS.app/Contents/MacOS/obslua.so
-            ./OBS.app/Contents/MacOS/_obspython.so
-            ./OBS.app/Contents/PlugIns/obs-x264.so
-            ./OBS.app/Contents/PlugIns/text-freetype2.so
-            ./OBS.app/Contents/PlugIns/obs-outputs.so
-          )
-
-          if ! [ "${{ env.MACOS_CEF_BUILD_VERSION }}" -le 3770 ]; then
-          ../CI/scripts/macos/app/dylibBundler -cd -of -a ./OBS.app -q -f \
-            -s ./OBS.app/Contents/MacOS \
-            -s /tmp/obsdeps/lib \
-            -s /tmp/obsdeps/lib/QtSvg.framework \
-            -s /tmp/obsdeps/lib/QtXml.framework \
-            -s /tmp/obsdeps/lib/QtNetwork.framework \
-            -s /tmp/obsdeps/lib/QtCore.framework \
-            -s /tmp/obsdeps/lib/QtGui.framework \
-            -s /tmp/obsdeps/lib/QtWidgets.framework \
-            -s /tmp/obsdeps/lib/QtDBus.framework \
-            -s /tmp/obsdeps/lib/QtPrintSupport.framework \
-            -s "${{ github.workspace }}/cmbuild/sparkle/Sparkle.framework" \
-            -s ./rundir/RelWithDebInfo/bin \
-            $(echo "${BUNDLE_PLUGINS[@]/#/-x }")
-          else
-          ../CI/scripts/macos/app/dylibBundler -cd -of -a ./OBS.app -q -f \
-            -s ./OBS.app/Contents/MacOS \
-            -s /tmp/obsdeps/lib \
-            -s /tmp/obsdeps/lib/QtSvg.framework \
-            -s /tmp/obsdeps/lib/QtXml.framework \
-            -s /tmp/obsdeps/lib/QtNetwork.framework \
-            -s /tmp/obsdeps/lib/QtCore.framework \
-            -s /tmp/obsdeps/lib/QtGui.framework \
-            -s /tmp/obsdeps/lib/QtWidgets.framework \
-            -s /tmp/obsdeps/lib/QtDBus.framework \
-            -s /tmp/obsdeps/lib/QtPrintSupport.framework \
-            -s "${{ github.workspace }}/cmbuild/sparkle/Sparkle.framework" \
-            -s ./rundir/RelWithDebInfo/bin \
-            $(echo "${BUNDLE_PLUGINS[@]/#/-x }") \
-            -x ./OBS.app/Contents/PlugIns/obs-browser-page
-          fi
-
-          mv ./libobs-opengl/libobs-opengl.so ./OBS.app/Contents/Frameworks
-
-          cp -R "${{ github.workspace }}/cmbuild/cef_binary_${{ env.MACOS_CEF_BUILD_VERSION }}_macos_x86_64/Release/Chromium Embedded Framework.framework" ./OBS.app/Contents/Frameworks/
-          chown -R $(whoami) ./OBS.app/Contents/Frameworks/
-
-          cp ../CI/scripts/macos/app/OBSPublicDSAKey.pem ./OBS.app/Contents/Resources
-
-          if [ "${GITHUB_REF:0:10}" = "refs/tags/" ]; then
-            plutil -insert CFBundleVersion -string ${{ env.OBS_GIT_TAG }} ./OBS.app/Contents/Info.plist
-            plutil -insert CFBundleShortVersionString -string ${{ env.OBS_GIT_TAG }} ./OBS.app/Contents/Info.plist
-          else
-            plutil -insert CFBundleVersion -string ${{ env.OBS_GIT_TAG }}-${{ env.OBS_GIT_HASH }} ./OBS.app/Contents/Info.plist
-            plutil -insert CFBundleShortVersionString -string ${{ env.OBS_GIT_TAG }}-${{ env.OBS_GIT_HASH }} ./OBS.app/Contents/Info.plist
-          fi
-
-          plutil -insert OBSFeedsURL -string https://obsproject.com/osx_update/feeds.xml ./OBS.app/Contents/Info.plist
-          plutil -insert SUFeedURL -string https://obsproject.com/osx_update/stable/updates.xml ./OBS.app/Contents/Info.plist
-          plutil -insert SUPublicDSAKeyFile -string OBSPublicDSAKey.pem ./OBS.app/Contents/Info.plist
-
-          codesign --force --options runtime --sign "${SIGN_IDENTITY:--}" "./OBS.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop"
-          codesign --force --options runtime --sign "${SIGN_IDENTITY:--}" "./OBS.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate"
-          codesign --force --options runtime --sign "${SIGN_IDENTITY:--}" --deep ./OBS.app/Contents/Frameworks/Sparkle.framework
+          echo "CODESIGN_IDENT=${{ secrets.MACOS_SIGNING_IDENTITY }}" >> $GITHUB_ENV
+          echo "BUILD_FOR_DISTRIBUTION=ON" >> $GITHUB_ENV
 
-          codesign --force --options runtime --entitlements "../CI/scripts/macos/app/entitlements.plist" --sign "${SIGN_IDENTITY:--}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libEGL.dylib"
-          codesign --force --options runtime --entitlements "../CI/scripts/macos/app/entitlements.plist" --sign "${SIGN_IDENTITY:--}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libswiftshader_libEGL.dylib"
-          codesign --force --options runtime --entitlements "../CI/scripts/macos/app/entitlements.plist" --sign "${SIGN_IDENTITY:--}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libGLESv2.dylib"
-          codesign --force --options runtime --entitlements "../CI/scripts/macos/app/entitlements.plist" --sign "${SIGN_IDENTITY:--}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libswiftshader_libGLESv2.dylib"
-          if ! [ "${{ env.MACOS_CEF_BUILD_VERSION }}" -le 3770 ]; then
-            codesign --force --options runtime --entitlements "../CI/scripts/macos/app/entitlements.plist" --sign "${SIGN_IDENTITY:--}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libvk_swiftshader.dylib"
-          fi
-
-          if ! [ "${{ env.MACOS_CEF_BUILD_VERSION }}" -le 3770 ]; then
-            codesign --force --options runtime --entitlements "../CI/scripts/macos/helpers/helper-entitlements.plist" --sign "${SIGN_IDENTITY:--}" --deep "./OBS.app/Contents/Frameworks/OBS Helper.app"
-            codesign --force --options runtime --entitlements "../CI/scripts/macos/helpers/helper-gpu-entitlements.plist" --sign "${SIGN_IDENTITY:--}" --deep "./OBS.app/Contents/Frameworks/OBS Helper (GPU).app"
-            codesign --force --options runtime --entitlements "../CI/scripts/macos/helpers/helper-plugin-entitlements.plist" --sign "${SIGN_IDENTITY:--}" --deep "./OBS.app/Contents/Frameworks/OBS Helper (Plugin).app"
-            codesign --force --options runtime --entitlements "../CI/scripts/macos/helpers/helper-renderer-entitlements.plist" --sign "${SIGN_IDENTITY:--}" --deep "./OBS.app/Contents/Frameworks/OBS Helper (Renderer).app"
-          fi
-
-          codesign --force --options runtime --deep --sign "${SIGN_IDENTITY:--}" "./OBS.app/Contents/Resources/data/obs-plugins/mac-virtualcam/obs-mac-virtualcam.plugin"
+      - name: 'Build OBS'
+        run: CI/macos/02_build_obs.sh --codesign --architecture "${{ matrix.arch }}"
 
-          codesign --force --options runtime --entitlements "../CI/scripts/macos/app/entitlements.plist" --sign "${SIGN_IDENTITY:--}" --deep ./OBS.app
+      - name: 'Run tests'
+        if: success()
+        run: cmake --build build -t test
 
-          codesign -dvv ./OBS.app
-      - name: 'Package'
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        working-directory: ${{ github.workspace }}/build
-        shell: bash
-        run: |
-          FILE_DATE=$(date +%Y-%m-%d)
-          FILE_NAME=$FILE_DATE-${{ env.OBS_GIT_HASH }}-${{ env.OBS_GIT_TAG }}-macOS.dmg
-          echo "FILE_NAME=${FILE_NAME}" >> $GITHUB_ENV
-
-          cp ../CI/scripts/macos/package/settings.json.template ./settings.json
-          /usr/bin/sed -i '' 's#\$\$VERSION\$\$#${{ env.OBS_GIT_TAG }}#g' ./settings.json
-          /usr/bin/sed -i '' 's#\$\$CI_PATH\$\$#../CI/scripts/macos#g' ./settings.json
-          /usr/bin/sed -i '' 's#\$\$BUNDLE_PATH\$\$#${{ github.workspace }}/build#g' ./settings.json
-
-          dmgbuild "OBS-Studio ${{ env.OBS_GIT_TAG }}" "${FILE_NAME}" -s ./settings.json
-          mkdir ../nightly
-          codesign --force --sign "${SIGN_IDENTITY:--}" ./"${FILE_NAME}"
-          codesign -dvv ./"${FILE_NAME}"
-          sudo cp ./${FILE_NAME} ../nightly/${FILE_NAME}
-      - name: 'Publish'
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        uses: actions/[email protected]
-        with:
-          name: '${{ env.FILE_NAME }}'
-          path: ./nightly/*.dmg
-      - name: 'Package Release'
-        if: success() && startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' && env.HAVE_CODESIGN_IDENTITY == 'true'
-        working-directory: ${{ github.workspace }}/build
-        shell: bash
+      - name: 'Create build artifact'
+        if: ${{ success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1') }}
         run: |
-          FILE_DATE=$(date +%Y-%m-%d)
-          FILE_NAME=$FILE_DATE-${{ env.OBS_GIT_HASH }}-${{ env.OBS_GIT_TAG }}-macOS.dmg
-          RELEASE_FILE_NAME=$FILE_DATE-${{ env.OBS_GIT_HASH }}-${{ env.OBS_GIT_TAG }}-rel-macOS.dmg
-          echo "RELEASE_FILE_NAME=${RELEASE_FILE_NAME}" >> $GITHUB_ENV
-
-          xcrun altool --store-password-in-keychain-item "AC_PASSWORD" -u "${{ secrets.MACOS_NOTARIZATION_USERNAME }}" -p "${{ secrets.MACOS_NOTARIZATION_PASSWORD }}"
+          CI/macos/03_package_obs.sh --codesign
+          ARTIFACT_NAME=$(basename $(/usr/bin/find build -type f -name "obs-studio-*.dmg" -depth 1 | head -1))
+          echo "FILE_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
 
-          xcnotary precheck "./OBS.app"
-
-          if [ "$?" -eq 0 ]; then
-            xcnotary notarize "$FILE_NAME" --developer-account "${{ secrets.MACOS_NOTARIZATION_USERNAME }}" --developer-password-keychain-item "AC_PASSWORD" --provider "${{ secrets.ASC_PROVIDER_SHORTNAME }}"
-          else
-            return 1
-          fi
-
-          mkdir ../release
-          sudo mv ./$FILE_NAME ../release/$RELEASE_FILE_NAME
-      - name: 'Publish Release'
-        if: success() && startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' && env.HAVE_CODESIGN_IDENTITY == 'true'
-        uses: actions/[email protected]
+      - name: 'Upload build Artifact'
+        if: ${{ success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1') }}
+        uses: actions/upload-artifact@v2
         with:
-          name: '${{ env.RELEASE_FILE_NAME }}'
-          path: ./release/*.dmg
-  ubuntu64:
-    name: 'Linux/Ubuntu 64-bit'
-    runs-on: [ubuntu-18.04]
+          name: 'obs-macos-${{ matrix.arch }}'
+          path: '${{ github.workspace }}/obs-studio/build/${{ env.FILE_NAME }}'
+
+  linux_build:
+    name: '02 - Linux'
+    runs-on: ${{ matrix.ubuntu }}
+    strategy:
+      matrix:
+        ubuntu: ['ubuntu-20.04', 'ubuntu-18.04']
+    if: always()
+    needs: [clang_check]
+    defaults:
+      run:
+        shell: bash
+        working-directory: 'obs-studio'
+    env:
+      BUILD_FOR_DISTRIBUTION: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' }}
     steps:
       - name: 'Checkout'
         uses: actions/[email protected]
         with:
           submodules: 'recursive'
-      - name: 'Fetch Git Tags'
-        run: |
-          git fetch --prune --unshallow
-          echo "OBS_GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_ENV
-          echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
-          echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
+          path: 'obs-studio'
+          fetch-depth: 0
+
       - name: 'Check for Github Labels'
         if: github.event_name == 'pull_request'
         run: |
-          LABELS_URL="$(echo ${{ github.event.pull_request.url }} | sed s'/pulls/issues/')"
-          LABEL_FOUND="$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "${LABELS_URL}/labels" | sed -n 's/.*"name": "\(.*\)",/\1/p' | grep 'Seeking Testers' || true)"
-          if [ "${LABEL_FOUND}" = "Seeking Testers" ]; then
+          if test -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')"; then
             echo "SEEKING_TESTERS=1" >> $GITHUB_ENV
           else
             echo "SEEKING_TESTERS=0" >> $GITHUB_ENV
           fi
-      - name: Install prerequisites (Apt)
-        shell: bash
-        run: |
-          echo "NPROC=$(($(nproc)+1))" >> $GITHUB_ENV
-          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 \
-           libsndio-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 \
-           qtbase5-private-dev \
-           libqt5svg5-dev \
-           swig \
-           libcmocka-dev \
-           libpci-dev
+
+          echo "CACHE_DATE=$(date +"%Y-%m-%d")" >> $GITHUB_ENV
+
+      - name: 'Restore ccache from cache'
+        id: ccache-cache
+        uses: actions/[email protected]
+        env:
+          CACHE_NAME: 'ccache-cache'
+        with:
+          path: ${{ github.workspace }}/.ccache
+          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ matrix.ubuntu }}-${{ env.CACHE_DATE }}
+
       - name: 'Restore Chromium Embedded Framework from cache'
         id: cef-cache
         uses: actions/[email protected]
         env:
           CACHE_NAME: 'cef-cache'
         with:
-          path: ${{ github.workspace }}/cmbuild/cef_binary_${{ env.LINUX_CEF_BUILD_VERSION }}_linux64
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.LINUX_CEF_BUILD_VERSION }}-1
-      - name: 'Install prerequisite: Chromium Embedded Framework'
-        if: steps.cef-cache.outputs.cache-hit != 'true'
-        shell: bash
-        run: |
-          curl -kL https://cdn-fastly.obsproject.com/downloads/cef_binary_${{ env.LINUX_CEF_BUILD_VERSION }}_linux64.tar.bz2 -f --retry 5 -o cef.tar.bz2
-          if [ ! -d "${{ github.workspace }}/cmbuild" ]; then mkdir "${{ github.workspace }}/cmbuild"; fi
-          tar -C"${{ github.workspace }}/cmbuild" -xjf cef.tar.bz2
-      - name: 'Configure'
-        shell: bash
-        run: |
-          mkdir ./build
-          cd ./build
-          cmake -DENABLE_PIPEWIRE=OFF -DUNIX_STRUCTURE=0 -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/obs-studio-portable" -DENABLE_UNIT_TESTS=ON -DENABLE_VLC=ON -DWITH_RTMPS=ON -DBUILD_BROWSER=ON -DCEF_ROOT_DIR="${{ github.workspace }}/cmbuild/cef_binary_${{ env.LINUX_CEF_BUILD_VERSION }}_linux64" -DTWITCH_CLIENTID='${{ env.TWITCH_CLIENTID }}' -DTWITCH_HASH='${{ env.TWITCH_HASH }}' -DRESTREAM_CLIENTID='${{ env.RESTREAM_CLIENTID }}' -DRESTREAM_HASH='${{ env.RESTREAM_HASH }}' ..
-      - name: 'Build'
-        shell: bash
-        working-directory: ${{ github.workspace }}/build
-        run: make -j${NPROC:-4}
-      - name: 'Test'
-        shell: bash
-        working-directory: ${{ github.workspace }}/build
-        run: make CTEST_OUTPUT_ON_FAILURE=1 test
-      - name: 'Package'
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        shell: bash
+          path: ${{ github.workspace }}/obs-build-dependencies/cef_binary_${{ env.CEF_BUILD_VERSION_LINUX }}_linux64
+          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.CEF_BUILD_VERSION_LINUX }}
+
+      - name: 'Install dependencies'
+        env:
+          RESTORED_CEF: ${{ steps.cef-cache.outputs.cache-hit }}
+        run: CI/linux/01_install_dependencies.sh --disable-pipewire
+
+      - name: 'Build OBS'
+        run: CI/linux/02_build_obs.sh --disable-pipewire
+
+      - name: 'Run tests'
+        if: success()
+        run: cmake --build build -t test
+
+      - name: 'Create build artifact'
+        if: ${{ success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1') }}
         run: |
-          FILE_DATE=$(date +%Y-%m-%d)
-          FILE_NAME=$FILE_DATE-${{ env.OBS_GIT_HASH }}-${{ env.OBS_GIT_TAG }}-linux64.tar.gz
-          echo "FILE_NAME=${FILE_NAME}" >> $GITHUB_ENV
-          cd ./build
-          sudo checkinstall --default --install=no --pkgname=obs-studio --fstrans=yes --backup=no --pkgversion="$(date +%Y%m%d)-git" --deldoc=yes
-          mkdir ../nightly
-          tar -cvzf "${FILE_NAME}" *.deb
-          mv "${FILE_NAME}" ../nightly/
-          cd -
-      - name: 'Publish'
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        uses: actions/[email protected]
+          CI/linux/03_package_obs.sh
+          ARTIFACT_NAME=$(basename $(/usr/bin/find build  -maxdepth 1 -type f -name "obs-studio-*.deb" | sort -rn | head -1))
+          echo "FILE_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
+
+      - name: 'Upload build Artifact'
+        if: ${{ success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1') }}
+        uses: actions/upload-artifact@v2
         with:
-          name: '${{ env.FILE_NAME }}'
-          path: './nightly/*.tar.gz'
-  win64:
-    name: 'Windows 64-bit'
+          name: 'obs-linux-deb'
+          path: '${{ github.workspace }}/obs-studio/build/${{ env.FILE_NAME }}'
+
+  windows_build:
+    name: '02 - Windows'
     runs-on: [windows-2019]
+    needs: [clang_check]
+    if: always()
+    strategy:
+      matrix:
+        arch: [64, 32]
     env:
-      QT_VERSION: '5.15.2'
-      CMAKE_GENERATOR: "Visual Studio 16 2019"
-      CMAKE_SYSTEM_VERSION: "10.0.18363.657"
-      WINDOWS_DEPS_VERSION: '2022-01-31'
-      WINDOWS_DEPS_CACHE_VERSION: '1'
-      VLC_VERSION: '3.0.0-git'
-      VIRTUALCAM-GUID: "A3FCE0F5-3493-419F-958A-ABA1250EC20B"
+      CMAKE_GENERATOR: 'Visual Studio 16 2019'
+      CMAKE_SYSTEM_VERSION: '10.0.18363.657'
+      VIRTUALCAM-GUID: 'A3FCE0F5-3493-419F-958A-ABA1250EC20B'
+      BUILD_FOR_DISTRIBUTION: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' }}
+    defaults:
+      run:
+        working-directory: 'obs-studio'
     steps:
-      - name: 'Add msbuild to PATH'
-        uses: microsoft/[email protected]
       - name: 'Checkout'
         uses: actions/[email protected]
         with:
           submodules: 'recursive'
-      - name: 'Fetch Git Tags'
-        shell: bash
-        run: |
-          git fetch --prune --unshallow
-          echo "OBS_GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_ENV
-          echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
-          echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
+          path: 'obs-studio'
+          fetch-depth: 0
+
+      - name: 'Add msbuild to PATH'
+        uses: microsoft/[email protected]
+
       - name: 'Check for Github Labels'
         if: github.event_name == 'pull_request'
-        shell: bash
         run: |
-          LABELS_URL="$(echo ${{ github.event.pull_request.url }} | sed s'/pulls/issues/')"
-          LABEL_FOUND="$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "${LABELS_URL}/labels" | sed -n 's/.*"name": "\(.*\)",/\1/p' | grep 'Seeking Testers' || true)"
-          if [ "${LABEL_FOUND}" = "Seeking Testers" ]; then
-            echo "SEEKING_TESTERS=1" >> $GITHUB_ENV
-          else
-            echo "SEEKING_TESTERS=0" >> $GITHUB_ENV
-          fi
-      - name: 'Restore QT dependency from cache'
-        id: qt-cache
-        uses: actions/[email protected]
-        env:
-          CACHE_NAME: 'windows-qt-cache'
-        with:
-          path: ${{ github.workspace }}/cmbuild/QT
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.QT_VERSION }}
-      - name: 'Restore pre-built dependencies from cache'
-        id: deps-cache
-        uses: actions/[email protected]
-        env:
-          CACHE_NAME: 'windows-deps-cache'
-        with:
-          path: ${{ github.workspace }}/cmbuild/deps
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_DEPS_VERSION }}-${{ env.WINDOWS_DEPS_CACHE_VERSION }}
+          $LabelFound = try { (Invoke-RestMethod -Authentication 'Bearer' -Token (ConvertTo-SecureString '${{ secrets.GITHUB_TOKEN }}' -AsPlainText) -Uri "${{ github.event.pull_request.url }}" -UseBasicParsing).labels.name.contains("Seeking Testers") } catch { $false }
+          Write-Output "SEEKING_TESTERS=$(if( $LabelFound -eq $true ) { 1 } else { 0 })" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
+
       - name: 'Restore VLC dependency from cache'
         id: vlc-cache
         uses: actions/[email protected]
         env:
-          CACHE_NAME: 'windows-vlc-cache'
+          CACHE_NAME: 'vlc-cache'
         with:
-          path: ${{ github.workspace }}/cmbuild/vlc
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.VLC_VERSION }}
-      - name: 'Restore CEF dependency from cache (64 bit)'
+          path: ${{ github.workspace }}/obs-build-dependencies/vlc-${{ env.VLC_VERSION_WIN }}
+          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.VLC_VERSION_WIN }}
+
+      - name: 'Restore Chromium Embedded Framework from cache'
         id: cef-cache
         uses: actions/[email protected]
         env:
-          CACHE_NAME: 'windows-cef-64-cache'
+          CACHE_NAME: 'cef-cache'
         with:
-          path: ${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x64
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-3
-      - name: 'Install prerequisite: QT'
-        if: steps.qt-cache.outputs.cache-hit != 'true'
-        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 dependencies'
-        if: steps.deps-cache.outputs.cache-hit != 'true'
-        run: |
-          curl -L -O https://github.com/obsproject/obs-deps/releases/download/win-${{ env.WINDOWS_DEPS_VERSION }}/windows-deps-${{ env.WINDOWS_DEPS_VERSION }}.zip --retry 5 -C -
-          7z x windows-deps-${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}/cmbuild/deps"
-      - name: 'Install prerequisite: VLC'
-        if: steps.vlc-cache.outputs.cache-hit != 'true'
-        run: |
-          curl -kL https://cdn-fastly.obsproject.com/downloads/vlc.zip -f --retry 5 -o vlc.zip
-          7z x vlc.zip -o"${{ github.workspace }}/cmbuild/vlc"
-      - name: 'Install prerequisite: Chromium Embedded Framework'
-        if: steps.cef-cache.outputs.cache-hit != 'true'
-        run: |
-          curl -kL https://cdn-fastly.obsproject.com/downloads/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x64.zip -f --retry 5 -o cef.zip
-          7z x cef.zip -o"${{ github.workspace }}/cmbuild"
-      - name: 'Configure'
-        run: |
-          mkdir ./build
-          mkdir ./build64
-          cd ./build64
-          cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=true -DCOMPILE_D3D12_HOOK=true -DVLCPath="${{ github.workspace }}/cmbuild/vlc" -DDepsPath="${{ github.workspace }}/cmbuild/deps/win64" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2019_64" -DENABLE_VLC=ON -DCEF_ROOT_DIR="${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x64" -DTWITCH_CLIENTID='${{ env.TWITCH_CLIENTID }}' -DTWITCH_HASH='${{ env.TWITCH_HASH }}' -DRESTREAM_CLIENTID='${{ env.RESTREAM_CLIENTID }}' -DRESTREAM_HASH='${{ env.RESTREAM_HASH }}' -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE -DVIRTUALCAM_GUID=${{ env.VIRTUALCAM-GUID }} ..
-      - name: 'Build'
-        run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-studio.sln
-      - name: 'Package'
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
+          path: ${{ github.workspace }}/obs-build-dependencies/cef_binary_${{ env.CEF_BUILD_VERSION_WIN }}_windows${{ matrix.arch }}_minimal
+          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.CEF_BUILD_VERSION_WIN }}
+
+      - name: 'Install dependencies'
+        env:
+          RESTORED_VLC: ${{ steps.vlc-cache.outputs.cache-hit }}
+          RESTORED_CEF: ${{ steps.cef-cache.outputs.cache-hit }}
+        run: CI/windows/01_install_dependencies.ps1 -BuildArch ${{ matrix.arch }}-bit
+
+      - name: 'Build OBS'
+        run: CI/windows/02_build_obs.ps1 -BuildArch ${{ matrix.arch }}-bit
+
+      - name: 'Create build artifact'
+        if: ${{ success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1') }}
         run: |
-          $env:FILE_DATE=(Get-Date -UFormat "%F")
-          $env:FILE_NAME="${env:FILE_DATE}-${{ env.OBS_GIT_HASH }}-${{ env.OBS_GIT_TAG }}-win64"
-          echo "FILE_NAME=${env:FILE_NAME}" >> ${env:GITHUB_ENV}
-          robocopy .\build64\rundir\RelWithDebInfo .\build\ /E /XF .gitignore
-          7z
-      - name: 'Publish'
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        uses: actions/[email protected]
+          CI/windows/03_package_obs.ps1 -BuildArch ${{ matrix.arch }}-bit -Package
+          $ArtifactName = Get-ChildItem -filter "OBS-Studio-*-Win${{ matrix.arch }}.zip" -File
+          Write-Output "FILE_NAME=${ArtifactName}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
+
+      - name: 'Upload build artifact'
+        if: ${{ success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1') }}
+        uses: actions/upload-artifact@v2
         with:
-          name: '${{ env.FILE_NAME }}'
-          path: build/*
-  win32:
-    name: 'Windows 32-bit'
-    runs-on: [windows-2019]
-    env:
-      QT_VERSION: '5.15.2'
-      CMAKE_GENERATOR: "Visual Studio 16 2019"
-      CMAKE_SYSTEM_VERSION: "10.0.18363.657"
-      WINDOWS_DEPS_VERSION: '2022-01-31'
-      WINDOWS_DEPS_CACHE_VERSION: '1'
-      VIRTUALCAM-GUID: "A3FCE0F5-3493-419F-958A-ABA1250EC20B"
+          name: 'obs-win${{ matrix.arch }}'
+          path: '${{ env.FILE_NAME }}'
+
+  linux_package:
+    name: '02 - Flatpak Bundle'
+    runs-on: [ubuntu-latest]
+    needs: [clang_check]
+    if: always()
+    defaults:
+      run:
+        shell: bash
+    container:
+      image: bilelmoussaoui/flatpak-github-actions:kde-5.15-21.08
+      options: --privileged
     steps:
-      - name: 'Add msbuild to PATH'
-        uses: microsoft/[email protected]
+      - name: 'Check for Github Labels'
+        if: github.event_name == 'pull_request'
+        run: |
+          if ! /usr/bin/command -v "jq" >/dev/null 2>&1; then sudo dnf install -y -q jq; fi
+          if test -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')"; then
+            echo "SEEKING_TESTERS=1" >> $GITHUB_ENV
+          else
+            echo "SEEKING_TESTERS=0" >> $GITHUB_ENV
+          fi
+
       - name: 'Checkout'
         uses: actions/[email protected]
+        if: ${{ success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1') }}
         with:
           submodules: 'recursive'
-      - name: 'Fetch Git Tags'
-        shell: bash
+          fetch-depth: 0
+
+      - name: 'Setup build environment'
+        if: ${{ success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1') }}
         run: |
-          git fetch --prune --unshallow
           echo "OBS_GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_ENV
           echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
           echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
-      - name: 'Check for Github Labels'
-        if: github.event_name == 'pull_request'
-        shell: bash
+
+      - name: Build Flatpak Manifest
+        uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v4
+        if: ${{ success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1') }}
+        with:
+          bundle: obs-studio-${{ github.sha }}.flatpak
+          manifest-path: CI/flatpak/com.obsproject.Studio.json
+          cache-key: flatpak-builder-${{ github.sha }}
+
+  windows_package:
+    name: '03 - Windows Installer'
+    runs-on: [windows-latest]
+    needs: [windows_build]
+    if: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' }}
+    env:
+      BUILD_FOR_DISTRIBUTION: 'ON'
+    steps:
+      - name: 'Checkout'
+        uses: actions/[email protected]
+
+      - name: 'Add msbuild to PATH'
+        uses: microsoft/[email protected]
+
+      - name: 'Download 64-bit artifact'
+        uses: actions/download-artifact@v2
+        with:
+          name: 'obs-win64'
+
+      - name: 'Download 32-bit artifact'
+        uses: actions/download-artifact@v2
+        with:
+          name: 'obs-win32'
+
+      - name: 'Unpack Windows build artifacts'
         run: |
-          LABELS_URL="$(echo ${{ github.event.pull_request.url }} | sed s'/pulls/issues/')"
-          LABEL_FOUND="$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "${LABELS_URL}/labels" | sed -n 's/.*"name": "\(.*\)",/\1/p' | grep 'Seeking Testers' || true)"
-          if [ "${LABEL_FOUND}" = "Seeking Testers" ]; then
-            echo "SEEKING_TESTERS=1" >> $GITHUB_ENV
-          else
-            echo "SEEKING_TESTERS=0" >> $GITHUB_ENV
-          fi
-      - name: 'Restore QT dependency from cache'
-        id: qt-cache
-        uses: actions/[email protected]
-        env:
-          CACHE_NAME: 'qt-cache'
+          if (!(Test-Path install_temp)) {
+            $null = New-Item -ItemType Directory -Force -Path install_temp
+          }
+
+          Expand-Archive -Path "$(Get-ChildItem -filter "OBS-Studio-*-Win32.zip" -File)" -DestinationPath install_temp
+          Expand-Archive -Path "$(Get-ChildItem -filter "OBS-Studio-*-Win64.zip" -File)" -Force -DestinationPath install_temp
+
+          CI/windows/03_package_obs.ps1 -CombinedArchs -Package
+
+          $ArtifactName = (Get-ChildItem -filter "OBS-Studio-*-Windows.zip" -File).Name
+          Write-Output "FILE_NAME=${ArtifactName}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
+
+      - name: 'Upload build artifact'
+        uses: actions/upload-artifact@v2
         with:
-          path: ${{ github.workspace }}/cmbuild/QT
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.QT_VERSION }}
-      - name: 'Restore pre-built dependencies from cache'
-        id: deps-cache
-        uses: actions/[email protected]
-        env:
-          CACHE_NAME: 'deps-cache'
+          name: 'obs-windows'
+          path: '${{ env.FILE_NAME }}'
+
+  macos_release:
+    name: '03 - macOS notarized image'
+    runs-on: [macos-11]
+    needs: [macos_build]
+    env:
+      HAVE_CODESIGN_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY != '' && secrets.MACOS_SIGNING_CERT != '' }}
+      BUILD_FOR_DISTRIBUTION: 'ON'
+    if: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' }}
+    strategy:
+      matrix:
+        arch: ['x86_64']
+    defaults:
+      run:
+        shell: bash
+    steps:
+      - name: 'Checkout'
+        if: env.HAVE_CODESIGN_IDENTITY == 'true'
+        uses: actions/[email protected]
+
+      - name: 'Download artifact'
+        if: env.HAVE_CODESIGN_IDENTITY == 'true'
+        uses: actions/download-artifact@v2
         with:
-          path: ${{ github.workspace }}/cmbuild/deps
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_DEPS_VERSION }}-${{ env.WINDOWS_DEPS_CACHE_VERSION }}
-      - name: 'Restore VLC dependency from cache'
-        id: vlc-cache
-        uses: actions/[email protected]
-        env:
-          CACHE_NAME: 'vlc-cache'
+          name: 'obs-macos-${{ matrix.arch }}'
+
+      - name: 'Install Apple Developer Certificate'
+        if: env.HAVE_CODESIGN_IDENTITY == 'true'
+        uses: apple-actions/import-codesign-certs@253ddeeac23f2bdad1646faac5c8c2832e800071
         with:
-          path: ${{ github.workspace }}/cmbuild/vlc
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_VLC_VERSION }}
-      - name: 'Restore CEF dependency from cache (32 bit)'
-        id: cef-cache
-        uses: actions/[email protected]
+          p12-file-base64: ${{ secrets.MACOS_SIGNING_CERT }}
+          p12-password: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }}
+
+      - name: 'Create disk image for distribution'
+        if: env.HAVE_CODESIGN_IDENTITY == 'true'
         env:
-          CACHE_NAME: 'cef-32-cache'
-        with:
-          path: ${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x86
-          key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_CEF_BUILD_VERSION }}-3
-      - name: 'Install prerequisite: QT'
-        if: steps.qt-cache.outputs.cache-hit != 'true'
+          CODESIGN_IDENT: ${{ secrets.MACOS_SIGNING_IDENTITY }}
+          CODESIGN_IDENT_USER: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
+          CODESIGN_IDENT_PASS: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
         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 dependencies'
-        if: steps.deps-cache.outputs.cache-hit != 'true'
-        run: |
-          curl -L -O https://github.com/obsproject/obs-deps/releases/download/win-${{ env.WINDOWS_DEPS_VERSION }}/windows-deps-${{ env.WINDOWS_DEPS_VERSION }}.zip --retry 5 -C -
-          7z x windows-deps-${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}/cmbuild/deps"
-      - name: 'Install prerequisite: VLC'
-        if: steps.vlc-cache.outputs.cache-hit != 'true'
-        run: |
-          curl -kL https://cdn-fastly.obsproject.com/downloads/vlc.zip -f --retry 5 -o vlc.zip
-          7z x vlc.zip -o"${{ github.workspace }}/cmbuild/vlc"
-      - name: 'Install prerequisite: Chromium Embedded Framework'
-        if: steps.cef-cache.outputs.cache-hit != 'true'
-        run: |
-          curl -kL https://cdn-fastly.obsproject.com/downloads/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x86.zip -f --retry 5 -o cef.zip
-          7z x cef.zip -o"${{ github.workspace }}/cmbuild"
-      - name: 'Configure'
-        run: |
-          mkdir ./build
-          mkdir ./build32
-          cd ./build32
-          cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DENABLE_VLC=ON -DBUILD_BROWSER=true -DCOMPILE_D3D12_HOOK=true -DVLCPath="${{ github.workspace }}/cmbuild/vlc" -DDepsPath="${{ github.workspace }}/cmbuild/deps/win32" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2019" -DCEF_ROOT_DIR="${{ github.workspace }}/cmbuild/cef_binary_${{ env.WINDOWS_CEF_BUILD_VERSION }}_windows_x86" -DTWITCH_CLIENTID='${{ env.TWITCH_CLIENTID }}' -DTWITCH_HASH='${{ env.TWITCH_HASH }}' -DRESTREAM_CLIENTID='${{ env.RESTREAM_CLIENTID }}' -DRESTREAM_HASH='${{ env.RESTREAM_HASH }}' -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE -DVIRTUALCAM_GUID=${{ env.VIRTUALCAM-GUID }} ..
-      - name: 'Build'
-        run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-studio.sln
-      - name: 'Package'
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        run: |
-          $env:FILE_DATE=(Get-Date -UFormat "%F")
-          $env:FILE_NAME="${env:FILE_DATE}-${{ env.OBS_GIT_HASH }}-${{ env.OBS_GIT_TAG }}-win32"
-          echo "FILE_NAME=${env:FILE_NAME}" >> ${env:GITHUB_ENV}
-          robocopy .\build32\rundir\RelWithDebInfo .\build\ /E /XF .gitignore
-          7z
-      - name: 'Publish'
-        if: success() && (github.event_name != 'pull_request' || env.SEEKING_TESTERS == '1')
-        uses: actions/[email protected]
+          ARTIFACT_NAME=$(/usr/bin/find . -type f -name "obs-studio-*.dmg" -depth 1 | head -1)
+          CI/macos/03_package_obs.sh --notarize-image ${ARTIFACT_NAME}
+
+          echo "FILE_NAME=$(basename ${ARTIFACT_NAME})" >> $GITHUB_ENV
+
+      - name: 'Upload build Artifact'
+        if: env.HAVE_CODESIGN_IDENTITY == 'true'
+        uses: actions/upload-artifact@v2
         with:
-          name: '${{ env.FILE_NAME }}'
-          path: build/*
+          name: 'obs-macos-${{ matrix.arch }}-notarized'
+          path: '${{ github.workspace }}/${{ env.FILE_NAME }}'

+ 3 - 2
.gitignore

@@ -21,9 +21,11 @@
 *.ninja
 .ninja*
 .dirstamp
+/cmake/.CMakeBuildNumber
 
 #xcode
 *.xcodeproj/
+/xcodebuild/
 
 #clion
 .idea/
@@ -39,8 +41,7 @@ GeneratedFiles/
 .moc/
 /UI/obs.rc
 .vscode/
-
-
+/CI/include/*.lock.json
 
 /other/
 

+ 0 - 3
CI/before-deploy-win.cmd

@@ -1,3 +0,0 @@
-robocopy .\build32\rundir\RelWithDebInfo .\build\ /E /XF .gitignore
-robocopy .\build64\rundir\RelWithDebInfo .\build\ /E /XC /XN /XO /XF .gitignore
-7z a build.zip .\build\*

+ 0 - 6
CI/before-script-linux.sh

@@ -1,6 +0,0 @@
-#!/bin/bash
-
-set -ex
-ccache -s || echo "CCache is not available."
-mkdir build && cd build
-cmake -DENABLE_PIPEWIRE=OFF -DBUILD_BROWSER=ON -DCEF_ROOT_DIR="../cef_binary_${LINUX_CEF_BUILD_VERSION}_linux64" ..

+ 98 - 0
CI/build-freebsd.sh

@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+
+##############################################################################
+# FreeBSD full build script
+##############################################################################
+#
+# This script contains all steps necessary to:
+#
+#   * Build OBS with all default plugins and dependencies
+#   * Package a FreeBSD package
+#
+# Parameters:
+#   -h, --help                     : Print usage help
+#   -q, --quiet                    : Suppress most build process output
+#   -v, --verbose                  : Enable more verbose build process output
+#   -d, --skip-dependency-checks   : Skip dependency checks (default: off)
+#   -p, --portable                 : Create portable build (default: off)
+#   -pkg, --package                : Create distributable archive
+#                                    (default: off)
+#   --build-dir                    : Specify alternative build directory
+#                                    (default: build)"
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+## SET UP ENVIRONMENT ##
+_RUN_OBS_BUILD_SCRIPT=TRUE
+PRODUCT_NAME="OBS-Studio"
+
+CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
+DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+source "${CHECKOUT_DIR}/CI/include/build_support_freebsd.sh"
+
+## DEPENDENCY INSTALLATION
+source "${CHECKOUT_DIR}/CI/freebsd/01_install_dependencies.sh"
+
+## BUILD OBS ##
+source "${CHECKOUT_DIR}/CI/freebsd/02_build_obs.sh"
+
+## PACKAGE OBS AND NOTARIZE ##
+source "${CHECKOUT_DIR}/CI/freebsd/03_package_obs.sh"
+
+## MAIN SCRIPT FUNCTIONS ##
+print_usage() {
+    echo "build-linux.sh - Build script for OBS-Studio\n"
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "-d, --skip-dependency-checks   : Skip dependency checks (default: off)\n" \
+            "-p, --portable                 : Create portable build (default: off)\n" \
+            "-pkg, --package                : Create distributable disk image (default: off)\n" \
+            "--build-dir                    : Specify alternative build directory (default: build)\n"
+}
+
+obs-build-main() {
+    while true; do
+        case "${1}" in
+            -h | --help ) print_usage; exit 0 ;;
+            -q | --quiet ) export QUIET=TRUE; shift ;;
+            -v | --verbose ) export VERBOSE=TRUE; shift ;;
+            -d | --skip-dependency-checks ) SKIP_DEP_CHECKS=TRUE; shift ;;
+            -p | --portable ) PORTABLE=TRUE; shift ;;
+            -pkg | --package ) PACKAGE=TRUE; shift ;;
+            --disable-pipewire ) DISABLE_PIPEWIRE=TRUE; shift ;;
+            --build-dir ) BUILD_DIR="${2}"; shift 2 ;;
+            -- ) shift; break ;;
+            * ) break ;;
+        esac
+    done
+
+    ensure_dir "${CHECKOUT_DIR}"
+    step "Fetching OBS tags..."
+    git fetch origin --tags
+
+    GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
+    GIT_HASH=$(git rev-parse --short HEAD)
+    GIT_TAG=$(git describe --tags --abbrev=0)
+
+    FILE_NAME="obs-studio-${GIT_TAG}-${GIT_HASH}-FreeBSD"
+
+    if [ -z "${SKIP_DEP_CHECKS}" ]; then
+        install_dependencies
+    fi
+
+    build_obs
+
+    if [ "${PACKAGE}" ]; then
+        package_obs
+    fi
+
+    cleanup
+}
+
+obs-build-main $*

+ 105 - 0
CI/build-linux.sh

@@ -0,0 +1,105 @@
+#!/bin/bash
+
+##############################################################################
+# Linux full build script
+##############################################################################
+#
+# This script contains all steps necessary to:
+#
+#   * Build OBS with all default plugins and dependencies
+#   * Package a Linux deb package
+#
+# Parameters:
+#   -h, --help                     : Print usage help
+#   -q, --quiet                    : Suppress most build process output
+#   -v, --verbose                  : Enable more verbose build process output
+#   -d, --skip-dependency-checks   : Skip dependency checks (default: off)
+#   -p, --portable                 : Create portable build (default: off)
+#   -pkg, --package                : Create distributable disk image
+#                                    (default: off)
+#   --build-dir                    : Specify alternative build directory
+#                                    (default: build)"
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+## SET UP ENVIRONMENT ##
+_RUN_OBS_BUILD_SCRIPT=TRUE
+PRODUCT_NAME="OBS-Studio"
+
+CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
+DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh"
+
+## DEPENDENCY INSTALLATION
+source "${CHECKOUT_DIR}/CI/linux/01_install_dependencies.sh"
+
+## BUILD OBS ##
+source "${CHECKOUT_DIR}/CI/linux/02_build_obs.sh"
+
+## PACKAGE OBS AND NOTARIZE ##
+source "${CHECKOUT_DIR}/CI/linux/03_package_obs.sh"
+
+## MAIN SCRIPT FUNCTIONS ##
+print_usage() {
+    echo "build-linux.sh - Build script for OBS-Studio\n"
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "-d, --skip-dependency-checks   : Skip dependency checks (default: off)\n" \
+            "-p, --portable                 : Create portable build (default: off)\n" \
+            "-pkg, --package                : Create distributable disk image (default: off)\n" \
+            "--disable-pipewire             : Disable building with Pipewire support (default: off)\n" \
+            "--build-dir                    : Specify alternative build directory (default: build)\n"
+}
+
+obs-build-main() {
+    while true; do
+        case "${1}" in
+            -h | --help ) print_usage; exit 0 ;;
+            -q | --quiet ) export QUIET=TRUE; shift ;;
+            -v | --verbose ) export VERBOSE=TRUE; shift ;;
+            -d | --skip-dependency-checks ) SKIP_DEP_CHECKS=TRUE; shift ;;
+            -p | --portable ) PORTABLE=TRUE; shift ;;
+            -pkg | --package ) PACKAGE=TRUE; shift ;;
+            --disable-pipewire ) DISABLE_PIPEWIRE=TRUE; shift ;;
+            --build-dir ) BUILD_DIR="${2}"; shift 2 ;;
+            -- ) shift; break ;;
+            * ) break ;;
+        esac
+    done
+
+    ensure_dir "${CHECKOUT_DIR}"
+    step "Fetching OBS tags..."
+    git fetch origin --tags
+
+    GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
+    GIT_HASH=$(git rev-parse --short HEAD)
+    GIT_TAG=$(git describe --tags --abbrev=0)
+
+    if [ "${BUILD_FOR_DISTRIBUTION}" ]; then
+        VERSION_STRING="${GIT_TAG}"
+    else
+        VERSION_STRING="${GIT_TAG}-${GIT_HASH}"
+    fi
+
+    FILE_NAME="obs-studio-${VERSION_STRING}-Linux.deb"
+
+    if [ -z "${SKIP_DEP_CHECKS}" ]; then
+        install_dependencies
+    fi
+
+    build_obs
+
+    if [ "${PACKAGE}" ]; then
+        package_obs
+    fi
+
+    cleanup
+}
+
+obs-build-main $*

+ 155 - 0
CI/build-macos.sh

@@ -0,0 +1,155 @@
+#!/bin/bash
+
+##############################################################################
+# macOS build script
+##############################################################################
+#
+# This script contains all steps necessary to:
+#
+#   * Build OBS with all default plugins and dependencies
+#   * Create a macOS application bundle
+#   * Codesign the macOS application bundle
+#   * Package a macOS installation image
+#   * Notarize macOS application bundle and/or installation image
+#
+# Parameters:
+#   -h, --help                     : Print usage help
+#   -q, --quiet                    : Suppress most build process output
+#   -v, --verbose                  : Enable more verbose build process output
+#   -d, --skip-dependency-checks   : Skip dependency checks (default: off)
+#   -b, --bundle                   : Create relocatable application bundle
+#                                    (default: off)
+#   -p, --package                  : Create distributable disk image
+#                                    (default: off)
+#   -c, --codesign                 : Codesign OBS and all libraries
+#                                    (default: ad-hoc only)
+#   -n, --notarize                 : Notarize OBS (default: off)
+#   --xcode                        : Create Xcode build environment instead
+#                                    of Ninja
+#   --build-dir                    : Specify alternative build directory
+#                                    (default: build)"
+# Environment Variables (optional):
+#
+#   MACOS_DEPS_VERSION        : Precompiled macOS dependencies version
+#   MACOS_CEF_BUILD_VERSION   : Chromium Embedded Framework version
+#   VLC_VERSION               : VLC version
+#   SPARKLE_VERSION           : Sparkle Framework version
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+## SET UP ENVIRONMENT ##
+_RUN_OBS_BUILD_SCRIPT=TRUE
+PRODUCT_NAME="OBS-Studio"
+
+CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
+DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+source "${CHECKOUT_DIR}/CI/include/build_support_macos.sh"
+
+## INSTALL DEPENDENCIES ##
+source "${CHECKOUT_DIR}/CI/macos/01_install_dependencies.sh"
+
+## BUILD OBS ##
+source "${CHECKOUT_DIR}/CI/macos/02_build_obs.sh"
+
+## PACKAGE OBS AND NOTARIZE ##
+source "${CHECKOUT_DIR}/CI/macos/03_package_obs.sh"
+
+## MAIN SCRIPT FUNCTIONS ##
+print_usage() {
+    echo "build-macos.sh - Build script for OBS-Studio"
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "-d, --skip-dependency-checks   : Skip dependency checks (default: off)\n" \
+            "-b, --bundle                   : Create relocatable application bundle (default: off)\n" \
+            "-p, --package                  : Create distributable disk image (default: off)\n" \
+            "-c, --codesign                 : Codesign OBS and all libraries (default: ad-hoc only)\n" \
+            "-n, --notarize                 : Notarize OBS (default: off)\n" \
+            "--xcode                        : Create Xcode build environment instead of Ninja\n" \
+            "--build-dir                    : Specify alternative build directory (default: build)\n"
+}
+
+print_deprecation() {
+    echo -e "DEPRECATION ERROR:\n" \
+            "The '${1}' switch has been deprecated!\n"
+
+    if [ "${1}" = "-s" ]; then
+        echo -e "The macOS build script system has changed:\n" \
+                " - To configure and build OBS, run the script 'CI/macos/02_build_obs.sh'\n" \
+                " - To bundle OBS into a relocatable application bundle, run the script 'CI/macos/02_build_obs.sh --bundle\n" \
+                " - To package OBS, run the script 'CI/macos/03_package_obs.sh'\n" \
+                " - To notarize OBS, run the script 'CI/macos/03_package_obs.sh --notarize'\n"
+    fi
+
+}
+
+obs-build-main() {
+    while true; do
+        case "${1}" in
+            -h | --help ) print_usage; exit 0 ;;
+            -q | --quiet ) export QUIET=TRUE; shift ;;
+            -v | --verbose ) export VERBOSE=TRUE; shift ;;
+            -d | --skip-dependency-checks ) SKIP_DEP_CHECKS=TRUE; shift ;;
+            -p | --package ) PACKAGE=TRUE; shift ;;
+            -c | --codesign ) CODESIGN=TRUE; shift ;;
+            -n | --notarize ) NOTARIZE=TRUE; PACKAGE=TRUE CODESIGN=TRUE; shift ;;
+            -b | --bundle ) BUNDLE=TRUE; shift ;;
+            --xcode ) XCODE=TRUE; shift ;;
+            --build-dir ) BUILD_DIR="${2}"; shift 2 ;;
+            -s ) print_deprecation ${1}; exit 1 ;;
+            -- ) shift; break ;;
+            * ) break ;;
+        esac
+    done
+
+    ensure_dir "${CHECKOUT_DIR}"
+    check_archs
+    check_macos_version
+    step "Fetching OBS tags..."
+    /usr/bin/git fetch origin --tags
+
+    GIT_BRANCH=$(/usr/bin/git rev-parse --abbrev-ref HEAD)
+    GIT_HASH=$(/usr/bin/git rev-parse --short HEAD)
+    GIT_TAG=$(/usr/bin/git describe --tags --abbrev=0)
+
+    if [ "${BUILD_FOR_DISTRIBUTION}" ]; then
+        VERSION_STRING="${GIT_TAG}"
+    else
+        VERSION_STRING="${GIT_TAG}-${GIT_HASH}"
+    fi
+
+    if [ "${ARCH}" = "arm64" ]; then
+        FILE_NAME="obs-studio-${VERSION_STRING}-macOS-Apple.dmg"
+    elif [ "${ARCH}" = "universal" ]; then
+        FILE_NAME="obs-studio-${VERSION_STRING}-macOS.dmg"
+    else
+        FILE_NAME="obs-studio-${VERSION_STRING}-macOS-Intel.dmg"
+    fi
+
+    if [ -z "${SKIP_DEP_CHECKS}" ]; then
+        install_dependencies
+    fi
+
+    build_obs
+
+    if [ "${BUNDLE}" ]; then
+        bundle_obs
+    fi
+
+    if [ "${PACKAGE}" ]; then
+        package_obs
+    fi
+
+    if [ "${NOTARIZE}" ]; then
+        notarize_obs
+    fi
+
+    cleanup
+}
+
+obs-build-main $*

+ 137 - 0
CI/build-windows.ps1

@@ -0,0 +1,137 @@
+Param(
+    [Switch]$Help,
+    [Switch]$Quiet,
+    [Switch]$Verbose,
+    [Switch]$Package,
+    [Switch]$SkipDependencyChecks,
+    [Switch]$BuildInstaller,
+    [Switch]$CombinedArchs,
+    [String]$BuildDirectory = "build",
+    [ValidateSet("32-bit", "64-bit")]
+    [String]$BuildArch = (Get-CimInstance CIM_OperatingSystem).OSArchitecture,
+    [ValidateSet("Release", "RelWithDebInfo", "MinSizeRel", "Debug")]
+    [String]$BuildConfiguration = "RelWithDebInfo"
+)
+
+##############################################################################
+# Windows OBS build script
+##############################################################################
+#
+# This script contains all steps necessary to:
+#
+#   * Build OBS with all required dependencies
+#   * Create 64-bit and 32-bit variants
+#
+# Parameters:
+#   -Help                   : Print usage help
+#   -Quiet                  : Suppress most build process output
+#   -Verbose                : Enable more verbose build process output
+#   -SkipDependencyChecks   : Skip dependency checks
+#   -BuildDirectory         : Directory to use for builds
+#                             Default: build64 on 64-bit systems
+#                                      build32 on 32-bit systems
+#   -BuildArch              : Build architecture to use ("32-bit" or "64-bit")
+#   -BuildConfiguration     : Build configuration to use
+#                             Default: RelWithDebInfo
+#   -CombinedArchs          : Create combined packages and installer
+#                             (64-bit and 32-bit) - Default: off
+#   -Package                : Prepare folder structure for installer creation
+#
+# Environment Variables (optional):
+#  WindowsDepsVersion       : Pre-compiled Windows dependencies version
+#  WindowsQtVersion         : Pre-compiled Qt version
+#
+##############################################################################
+
+$ErrorActionPreference = "Stop"
+
+$_RunObsBuildScript = $true
+$ProductName = "OBS-Studio"
+
+$CheckoutDir = Resolve-Path -Path "$PSScriptRoot\.."
+$DepsBuildDir = "${CheckoutDir}/../obs-build-dependencies"
+$ObsBuildDir = "${CheckoutDir}/../obs-studio"
+
+. ${CheckoutDir}/CI/include/build_support_windows.ps1
+
+# Handle installation of build system components and build dependencies
+. ${CheckoutDir}/CI/windows/01_install_dependencies.ps1
+
+# Handle OBS build configuration
+. ${CheckoutDir}/CI/windows/02_build_obs.ps1
+
+# Handle packaging
+. ${CheckoutDir}/CI/windows/03_package_obs.ps1
+
+function Build-OBS-Main {
+    Ensure-Directory ${CheckoutDir}
+    Write-Step "Fetching version tags..."
+    $null = git fetch origin --tags
+    $GitBranch = git rev-parse --abbrev-ref HEAD
+    $GitHash = git rev-parse --short HEAD
+    $ErrorActionPreference = "SilentlyContinue"
+    $GitTag = git describe --tags --abbrev=0
+    $ErrorActionPreference = "Stop"
+
+    if(Test-Path variable:BUILD_FOR_DISTRIBUTION) {
+        $VersionString = "${GitTag}"
+    } else {
+        $VersionString = "${GitTag}-${GitHash}"
+    }
+
+    $FileName = "${ProductName}-${VersionString}"
+
+    if($CombinedArchs.isPresent) {
+        if (!(Test-Path env:obsInstallerTempDir)) {
+            $Env:obsInstallerTempDir = "${CheckoutDir}/install_temp"
+        }
+
+        if(!($SkipDependencyChecks.isPresent)) {
+            Install-Dependencies -BuildArch 64-bit
+        }
+
+        Build-OBS -BuildArch 64-bit
+
+        if(!($SkipDependencyChecks.isPresent)) {
+            Install-Dependencies -BuildArch 32-bit
+        }
+
+        Build-OBS -BuildArch 32-bit
+    } else {
+        if(!($SkipDependencyChecks.isPresent)) {
+            Install-Dependencies
+        }
+
+        Build-OBS
+    }
+
+    if($Package.isPresent) {
+        Package-OBS -CombinedArchs:$CombinedArchs
+    }
+}
+
+## MAIN SCRIPT FUNCTIONS ##
+function Print-Usage {
+    Write-Host "build-windows.ps1 - Build script for ${ProductName}"
+    $Lines = @(
+        "Usage: ${_ScriptName}",
+        "-Help                    : Print this help",
+        "-Quiet                   : Suppress most build process output"
+        "-Verbose                 : Enable more verbose build process output"
+        "-SkipDependencyChecks    : Skip dependency checks - Default: off",
+        "-BuildDirectory          : Directory to use for builds - Default: build64 on 64-bit systems, build32 on 32-bit systems",
+        "-BuildArch               : Build architecture to use ('32-bit' or '64-bit') - Default: local architecture",
+        "-BuildConfiguration      : Build configuration to use - Default: RelWithDebInfo",
+        "-CombinedArchs           : Create combined packages and installer (64-bit and 32-bit) - Default: off"
+        "-Package                 : Prepare folder structure for installer creation"
+    )
+    $Lines | Write-Host
+}
+
+$_ScriptName = "$($MyInvocation.MyCommand.Name)"
+if($Help.isPresent) {
+    Print-Usage
+    exit 0
+}
+
+Build-OBS-Main

+ 11 - 0
CI/check-changes.sh

@@ -0,0 +1,11 @@
+#!/bin/bash
+dirty=$(git ls-files --modified)
+
+set +x
+if [[ $dirty ]]; then
+	echo "================================="
+    echo "Files were not formatted properly"
+    echo "$dirty"
+    echo "================================="
+    exit 1
+fi

+ 51 - 0
CI/check-cmake.sh

@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o pipefail
+
+if [ ${#} -eq 1 -a "${1}" = "VERBOSE" ]; then
+    VERBOSITY="-l debug"
+else
+    VERBOSITY=""
+fi
+
+if [ "${CI}" ]; then
+    MODE="--check"
+else
+    MODE="-i"
+fi
+
+# Runs the formatter in parallel on the code base.
+# Return codes:
+#  - 1 there are files to be formatted
+#  - 0 everything looks fine
+
+# Get CPU count
+OS=$(uname)
+NPROC=1
+if [[ ${OS} = "Linux" ]] ; then
+    NPROC=$(nproc)
+elif [[ ${OS} = "Darwin" ]] ; then
+    NPROC=$(sysctl -n hw.physicalcpu)
+fi
+
+# Discover clang-format
+if ! type cmake-format 2> /dev/null ; then
+    echo "Required cmake-format not found"
+    exit 1
+fi
+
+find . -type d \( \
+    -path ./\*build -o \
+    -path ./deps/jansson -o \
+    -path ./plugins/decklink/\*/decklink-sdk -o \
+    -path ./plugins/enc-amf -o \
+    -path ./plugins/mac-syphon/syphon-framework -o \
+    -path ./plugins/obs-outputs/ftl-sdk -o \
+    -path ./plugins/obs-vst -o \
+    -path ./plugins/obs-browser -o \
+    -path ./plugins/win-dshow/libdshowcapture \
+\) -prune -false -type f -o \
+    -name 'CMakeLists.txt' -or \
+    -name '*.cmake' \
+ | xargs -L10 -P ${NPROC} cmake-format ${MODE} ${VERBOSITY}

+ 59 - 10
CI/check-format.sh

@@ -1,11 +1,60 @@
-#!/bin/bash
-dirty=$(git ls-files --modified)
-
-set +x
-if [[ $dirty ]]; then
-	echo "================================="
-    echo "Files were not formatted properly"
-    echo "$dirty"
-    echo "================================="
+#!/usr/bin/env bash
+# Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh
+
+set -o errexit
+set -o pipefail
+set -o nounset
+
+if [ ${#} -eq 1 ]; then
+    VERBOSITY="--verbose"
+else
+    VERBOSITY=""
+fi
+
+# Runs the Clang Formatter in parallel on the code base.
+# Return codes:
+#  - 1 there are files to be formatted
+#  - 0 everything looks fine
+
+# Get CPU count
+OS=$(uname)
+NPROC=1
+if [[ ${OS} = "Linux" ]] ; then
+    NPROC=$(nproc)
+elif [[ ${OS} = "Darwin" ]] ; then
+    NPROC=$(sysctl -n hw.physicalcpu)
+fi
+
+# Discover clang-format
+if type clang-format-12 2> /dev/null ; then
+    CLANG_FORMAT=clang-format-12
+elif type clang-format 2> /dev/null ; then
+    # Clang format found, but need to check version
+    CLANG_FORMAT=clang-format
+    V=$(clang-format --version)
+    if [[ $V != *"version 12.0"* ]]; then
+        echo "clang-format is not 12.0 (returned ${V})"
+        exit 1
+    fi
+else
+    echo "No appropriate clang-format found (expected clang-format-12.0.0, or clang-format)"
     exit 1
-fi
+fi
+
+find . -type d \( \
+    -path ./\*build -o \
+    -path ./cmake -o \
+    -path ./deps -o \
+    -path ./plugins/decklink/\*/decklink-sdk -o \
+    -path ./plugins/enc-amf -o \
+    -path ./plugins/mac-syphon/syphon-framework -o \
+    -path ./plugins/obs-outputs/ftl-sdk -o \
+    -path ./plugins/obs-vst \
+\) -prune -false -type f -o \
+    -name '*.h' -or \
+    -name '*.hpp' -or \
+    -name '*.m' -or \
+    -name '*.m,' -or \
+    -name '*.c' -or \
+    -name '*.cpp' \
+ | xargs -L100 -P ${NPROC} ${CLANG_FORMAT} ${VERBOSITY} -i -style=file -fallback-style=none

+ 5 - 4
CI/flatpak/com.obsproject.Studio.json

@@ -416,13 +416,14 @@
       "config-opts": [
         "-DCMAKE_BUILD_TYPE=Release",
         "-DENABLE_WAYLAND=ON",
-        "-DBUILD_BROWSER=ON",
+        "-DENABLE_BROWSER_SOURCE=ON",
         "-DCEF_ROOT_DIR=/app/cef",
-        "-DUNIX_STRUCTURE=ON",
         "-DUSE_XDG=ON",
-        "-DDISABLE_ALSA=ON",
+        "-DENABLE_ALSA=OFF",
         "-DENABLE_PULSEAUDIO=ON",
-        "-DWITH_RTMPS=ON"
+        "-DENABLE_RTMPS=ON",
+        "-DENABLE_VLC=OFF",
+        "-DENABLE_AJA=OFF"
       ],
       "secret-opts": [
         "-DRESTREAM_CLIENTID=$RESTREAM_CLIENTID",

+ 100 - 0
CI/freebsd/01_install_dependencies.sh

@@ -0,0 +1,100 @@
+#!/usr/bin/env bash
+
+##############################################################################
+# FreeBSD dependency management function
+##############################################################################
+#
+# This script file can be included in build scripts or run directly
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+install_build-deps() {
+    shift
+    status "Install OBS build dependencies"
+    trap "caught_error 'install_build-deps'" ERR
+
+    sudo pkg install -U -y $@
+}
+
+install_obs-deps() {
+    shift
+    status "Install OBS dependencies"
+    trap "caught_error 'install_obs-deps'" ERR
+
+    sudo pkg install -U -y $@
+}
+
+install_qt-deps() {
+    shift
+    status "Install Qt dependencies"
+    trap "caught_error 'install_qt-deps'" ERR
+
+    sudo pkg install -U -y $@
+}
+
+install_plugin-deps() {
+    shift
+    status "Install plugin dependencies"
+    trap "caught_error 'install_plugin-deps'" ERR
+
+    sudo pkg install -U -y $@
+}
+
+install_dependencies() {
+    status "Set up apt"
+    trap "caught_error 'install_dependencies'" ERR
+
+    BUILD_DEPS=(
+        "build-deps cmake ninja pkgconf curl ccache"
+        "obs-deps ffmpeg libx264 mbedtls mesa-libs jansson lua52 luajit python37 libX11 xorgproto libxcb \
+         libXcomposite libXext libXfixes libXinerama libXrandr swig dbus jansson libICE libSM libsysinfo"
+        "qt-deps qt5-buildtools qt5-qmake qt5-imageformats qt5-core qt5-gui qt5-svg qt5-widgets qt5-xml"
+        "plugin-deps v4l_compat fdk-aac fontconfig freetype2 speexdsp libudev-devd libv4l vlc audio/jack pulseaudio sndio"
+    )
+
+    for DEPENDENCY in "${BUILD_DEPS[@]}"; do
+        set -- ${DEPENDENCY}
+        trap "caught_error ${DEPENDENCY}" ERR
+        FUNC_NAME="install_${1}"
+        ${FUNC_NAME} ${@}
+    done
+}
+
+install-dependencies-standalone() {
+    CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
+    PRODUCT_NAME="OBS-Studio"
+    DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+    source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+    source "${CHECKOUT_DIR}/CI/include/build_support_freebsd.sh"
+
+    status "Setup of OBS build dependencies"
+    install_dependencies
+}
+
+print_usage() {
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n"
+}
+
+install-dependencies-main() {
+    if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
+        while true; do
+            case "${1}" in
+                -h | --help ) print_usage; exit 0 ;;
+                -q | --quiet ) export QUIET=TRUE; shift ;;
+                -v | --verbose ) export VERBOSE=TRUE; shift ;;
+                -- ) shift; break ;;
+                * ) break ;;
+            esac
+        done
+
+        install-dependencies-standalone
+    fi
+}
+
+install-dependencies-main $*

+ 120 - 0
CI/freebsd/02_build_obs.sh

@@ -0,0 +1,120 @@
+#!/usr/bin/env bash
+
+##############################################################################
+# FreeBSD build function
+##############################################################################
+#
+# This script file can be included in build scripts for FreeBSD or run
+# directly
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+build_obs() {
+    status "Build OBS"
+    if [ -z "${CI}" ]; then
+        _backup_artifacts
+    fi
+
+    step "Configure OBS..."
+    _configure_obs
+
+    ensure_dir "${CHECKOUT_DIR}/"
+    step "Build OBS targets..."
+    cmake --build ${BUILD_DIR}
+}
+
+# Function to configure OBS build
+_configure_obs() {
+    ensure_dir "${CHECKOUT_DIR}"
+    status "Configuration of OBS build system..."
+    check_ccache
+
+    if [ "${TWITCH_CLIENTID}" -a "${TWICH_HASH}" ]; then
+        TWITCH_OPTIONS="-DTWITCH_CLIENTID=\"${TWITCH_CLIENTID}\" -DTWITCH_HASH=\"${TWITCH_HASH}\""
+    fi
+
+    if [ "${RESTREAM_CLIENTID}" -a "${RESTREAM_HASH}" ]; then
+        RESTREAM_OPTIONS="-DRESTREAM_CLIENTID=\"${RESTREAM_CLIENTID}\" -DRESTREAM_HASH=\"${RESTREAM_HASH}\""
+    fi
+
+    if [ "${YOUTUBE_CLIENTID}" -a "${YOUTUBE_CLIENTID_HASH}" -a "${YOUTUBE_SECRET}" -a "{YOUTUBE_SECRET_HASH}" ]; then
+        YOUTUBE_OPTIONS="-DYOUTUBE_CLIENTID=\"${YOUTUBE_CLIENTID}\" -DYOUTUBE_CLIENTID_HASH=\"${YOUTUBE_CLIENTID_HASH}\" -DYOUTUBE_SECRET=\"${YOUTUBE_SECRET}\" -DYOUTUBE_SECRET_HASH=\"${YOUTUBE_SECRET_HASH}\""
+    fi
+
+    if [ "${PORTABLE}" ]; then
+        PORTABLE_BUILD="ON"
+    fi
+
+    cmake -S . -B ${BUILD_DIR} -G Ninja \
+        -DCMAKE_BUILD_TYPE=${BUILD_CONFIG} \
+        -DLINUX_PORTABLE=${PORTABLE_BUILD:-OFF} \
+        -DENABLE_PIPEWIRE=OFF \
+        ${CCACHE_OPTIONS} \
+        ${TWITCH_OPTIONS} \
+        ${YOUTUBE_OPTIONS} \
+        ${RESTREAM_OPTIONS} \
+        ${CI:+-DENABLE_UNIT_TESTS=ON -DBUILD_FOR_DISTRIBUTION=${BUILD_FOR_DISTRIBUTION} -DOBS_BUILD_NUMBER=${GITHUB_RUN_ID}} \
+        ${QUIET:+-Wno-deprecated -Wno-dev --log-level=ERROR}
+}
+
+# Function to backup previous build artifacts
+_backup_artifacts() {
+    ensure_dir "${CHECKOUT_DIR}"
+    if [ -d "${BUILD_DIR}" ]; then
+        status "Backup of old OBS build artifacts"
+
+        CUR_DATE=$(/bin/date +"%Y-%m-%d@%H%M%S")
+        NIGHTLY_DIR="${CHECKOUT_DIR}/nightly-${CUR_DATE}"
+        PACKAGE_NAME=$(/usr/bin/find ${BUILD_DIR} -maxdepth 1 -name "*.tar.gz" | sort -rn | head -1)
+
+        if [ "${PACKAGE_NAME}" ]; then
+            step "Back up $(basename "${PACKAGE_NAME}")..."
+            ensure_dir "${NIGHTLY_DIR}"
+            ensure_dir "${CHECKOUT_DIR}"
+            /usr/bin/find "${BUILD_DIR}" -maxdepth 1 \( -name "obs-studio-*.sh" -o -name "obs-studio-*.tar.gz" -o -name "obs-studio-*.tar.Z" \) -print0 | /usr/bin/xargs -0 -I {} /bin/mv {} ${NIGHTLY_DIR}/
+            info "You can find $(basename "${PACKAGE_NAME}") in ${NIGHTLY_DIR}"
+        fi
+    fi
+}
+
+build-obs-standalone() {
+    CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
+    PRODUCT_NAME="OBS-Studio"
+    DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+    source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+    source "${CHECKOUT_DIR}/CI/include/build_support_freebsd.sh"
+
+    build_obs
+}
+
+print_usage() {
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "-p, --portable                 : Create portable build (default: off)\n" \
+            "--build-dir                    : Specify alternative build directory (default: build)\n"
+}
+
+build-obs-main() {
+    if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
+        while true; do
+            case "${1}" in
+                -h | --help ) print_usage; exit 0 ;;
+                -q | --quiet ) export QUIET=TRUE; shift ;;
+                -v | --verbose ) export VERBOSE=TRUE; shift ;;
+                -p | --portable ) export PORTABLE=TRUE; shift ;;
+                --build-dir ) BUILD_DIR="${2}"; shift 2 ;;
+                -- ) shift; break ;;
+                * ) break ;;
+            esac
+        done
+
+        build-obs-standalone
+    fi
+}
+
+build-obs-main $*

+ 78 - 0
CI/freebsd/03_package_obs.sh

@@ -0,0 +1,78 @@
+#!/usr/bin/env bash
+
+##############################################################################
+# FreeBSD OBS package function
+##############################################################################
+#
+# This script file can be included in build scripts for FreeBSD or run directly
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+package_obs() {
+    status "Create FreeBSD debian package"
+    trap "caught_error 'package app'" ERR
+
+    ensure_dir "${CHECKOUT_DIR}"
+
+    step "Package OBS..."
+    cmake --build ${BUILD_DIR} -t package
+
+    ZIP_NAME="$(/usr/bin/find "${BUILD_DIR}" -maxdepth 1 -type f -name "obs-studio-*.sh" | sort -rn | head -1)"
+
+    if [ "${ZIP_NAME}" ]; then
+        mv "${ZIP_NAME%.*}.sh" "${BUILD_DIR}/${FILE_NAME}.sh"
+        mv "${ZIP_NAME%.*}.tar.gz" "${BUILD_DIR}/${FILE_NAME}.tar.gz"
+        mv "${ZIP_NAME%.*}.tar.Z" "${BUILD_DIR}/${FILE_NAME}.tar.Z"
+    else
+        error "ERROR No suitable OBS debian package generated"
+    fi
+}
+
+package-obs-standalone() {
+    PRODUCT_NAME="OBS-Studio"
+
+    CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
+    DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+    source ${CHECKOUT_DIR}/CI/include/build_support.sh
+    source ${CHECKOUT_DIR}/CI/include/build_support_freebsd.sh
+
+    step "Fetch OBS tags..."
+    git fetch origin --tags
+
+    GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
+    GIT_HASH=$(git rev-parse --short HEAD)
+    GIT_TAG=$(git describe --tags --abbrev=0)
+
+    FILE_NAME="obs-studio-${GIT_TAG}-${GIT_HASH}-FreeBSD"
+    package_obs
+}
+
+print_usage() {
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "--build-dir                    : Specify alternative build directory (default: build)\n"
+}
+
+package-obs-main() {
+    if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
+        while true; do
+            case "${1}" in
+                -h | --help ) print_usage; exit 0 ;;
+                -q | --quiet ) export QUIET=TRUE; shift ;;
+                -v | --verbose ) export VERBOSE=TRUE; shift ;;
+                --build-dir ) BUILD_DIR="${2}"; shift 2 ;;
+                -- ) shift; break ;;
+                * ) break ;;
+            esac
+        done
+
+        package-obs-standalone
+    fi
+}
+
+package-obs-main $*

+ 0 - 726
CI/full-build-macos.sh

@@ -1,726 +0,0 @@
-#!/bin/bash
-
-##############################################################################
-# macOS full build script
-##############################################################################
-#
-# This script contains all steps necessary to:
-#
-#   * Build OBS with all default plugins and dependencies
-#   * Create a macOS application bundle
-#   * Code-sign the macOS application-bundle
-#   * Package a macOS installation image
-#   * Notarize macOS application-bundle and/or installation image
-#
-# Parameters:
-#   -b: Create macOS bundle
-#   -d: Skip dependency checks
-#   -p: Create macOS distribution image
-#   -n: Notarize macOS app and disk image (implies bundling)
-#   -s: Skip the build process (useful for bundling/packaging only)
-#   -h: Print usage help
-#
-# Environment Variables (optional):
-#   MACOS_DEPS_VERSION        : Pre-compiled macOS dependencies version
-#   MACOS_CEF_BUILD_VERSION   : Chromium Embedded Framework version
-#   VLC_VERISON               : VLC version
-#   SPARKLE_VERSION           : Sparke Framework version
-#   BUILD_DIR                 : Alternative directory to build OBS in
-#
-##############################################################################
-
-# Halt on errors
-set -eE
-
-## SET UP ENVIRONMENT ##
-PRODUCT_NAME="OBS-Studio"
-
-CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
-DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
-BUILD_DIR="${BUILD_DIR:-build}"
-BUILD_CONFIG=${BUILD_CONFIG:-RelWithDebInfo}
-CI_SCRIPTS="${CHECKOUT_DIR}/CI/scripts/macos"
-CI_WORKFLOW="${CHECKOUT_DIR}/.github/workflows/main.yml"
-CI_MACOS_CEF_VERSION=$(/bin/cat "${CI_WORKFLOW}" | /usr/bin/sed -En "s/[ ]+MACOS_CEF_BUILD_VERSION: '([0-9]+)'/\1/p")
-CI_DEPS_VERSION=$(/bin/cat "${CI_WORKFLOW}" | /usr/bin/sed -En "s/[ ]+MACOS_DEPS_VERSION: '([0-9\-]+)'/\1/p")
-CI_VLC_VERSION=$(/bin/cat "${CI_WORKFLOW}" | /usr/bin/sed -En "s/[ ]+VLC_VERSION: '([0-9\.]+)'/\1/p")
-CI_SPARKLE_VERSION=$(/bin/cat "${CI_WORKFLOW}" | /usr/bin/sed -En "s/[ ]+SPARKLE_VERSION: '([0-9\.]+)'/\1/p")
-CI_QT_VERSION=$(/bin/cat "${CI_WORKFLOW}" | /usr/bin/sed -En "s/[ ]+QT_VERSION: '([0-9\.]+)'/\1/p" | /usr/bin/head -1)
-CI_MIN_MACOS_VERSION=$(/bin/cat "${CI_WORKFLOW}" | /usr/bin/sed -En "s/[ ]+MIN_MACOS_VERSION: '([0-9\.]+)'/\1/p")
-NPROC="${NPROC:-$(sysctl -n hw.ncpu)}"
-CURRENT_ARCH=$(uname -m)
-
-BUILD_DEPS=(
-    "obs-deps ${MACOS_DEPS_VERSION:-${CI_DEPS_VERSION}}"
-    "qt-deps ${QT_VERSION:-${CI_QT_VERSION}} ${MACOS_DEPS_VERSION:-${CI_DEPS_VERSION}}"
-    "cef ${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}"
-    "vlc ${VLC_VERSION:-${CI_VLC_VERSION}}"
-    "sparkle ${SPARKLE_VERSION:-${CI_SPARKLE_VERSION}}"
-)
-
-if [ -n "${TERM-}" ]; then
-    COLOR_RED=$(/usr/bin/tput setaf 1)
-    COLOR_GREEN=$(/usr/bin/tput setaf 2)
-    COLOR_BLUE=$(/usr/bin/tput setaf 4)
-    COLOR_ORANGE=$(/usr/bin/tput setaf 3)
-    COLOR_RESET=$(/usr/bin/tput sgr0)
-else
-    COLOR_RED=""
-    COLOR_GREEN=""
-    COLOR_BLUE=""
-    COLOR_ORANGE=""
-    COLOR_RESET=""
-fi
-
-
-MACOS_VERSION="$(/usr/bin/sw_vers -productVersion)"
-MACOS_MAJOR="$(/bin/echo ${MACOS_VERSION} | /usr/bin/cut -d '.' -f 1)"
-MACOS_MINOR="$(/bin/echo ${MACOS_VERSION} | /usr/bin/cut -d '.' -f 2)"
-
-## DEFINE UTILITIES ##
-
-hr() {
-    /bin/echo "${COLOR_BLUE}[${PRODUCT_NAME}] ${1}${COLOR_RESET}"
-}
-
-step() {
-    /bin/echo "${COLOR_GREEN}  + ${1}${COLOR_RESET}"
-}
-
-info() {
-    /bin/echo "${COLOR_ORANGE} + ${1}${COLOR_RESET}"
-}
-
-error() {
-    /bin/echo "${COLOR_RED}  + ${1}${COLOR_RESET}"
-}
-
-exists() {
-  /usr/bin/command -v "$1" >/dev/null 2>&1
-}
-
-ensure_dir() {
-    [[ -n "${1}" ]] && /bin/mkdir -p "${1}" && builtin cd "${1}"
-}
-
-cleanup() {
-    /bin/rm -rf "${CHECKOUT_DIR}/${BUILD_DIR}/settings.json"
-    unset CODESIGN_IDENT
-    unset CODESIGN_IDENT_USER
-    unset CODESIGN_IDENT_PASS
-}
-
-caught_error() {
-    error "ERROR during build step: ${1}"
-    cleanup
-    exit 1
-}
-
-## CHECK AND INSTALL DEPENDENCIES ##
-check_macos_version() {
-    MIN_VERSION=${MIN_MACOS_VERSION:-${CI_MIN_MACOS_VERSION}}
-    MIN_MAJOR=$(/bin/echo ${MIN_VERSION} | /usr/bin/cut -d '.' -f 1)
-    MIN_MINOR=$(/bin/echo ${MIN_VERSION} | /usr/bin/cut -d '.' -f 2)
-
-    if [ "${MACOS_MAJOR}" -lt "11" ] && [ "${MACOS_MINOR}" -lt "${MIN_MINOR}" ]; then
-        error "WARNING: Minimum required macOS version is ${MIN_VERSION}, but running on ${MACOS_VERSION}"
-    fi
-}
-
-install_homebrew_deps() {
-    if ! exists brew; then
-        error "Homebrew not found - please install homebrew (https://brew.sh)"
-        exit 1
-    fi
-
-    if [ -d /usr/local/opt/[email protected] ]; then
-        brew uninstall [email protected]
-        brew untap local/openssl
-    fi
-
-    if [ -d /usr/local/opt/[email protected] ]; then
-        brew uninstall [email protected]
-        brew untap local/python2
-    fi
-
-    brew bundle --file "${CI_SCRIPTS}/Brewfile"
-
-    check_curl
-}
-
-check_curl() {
-    if [ "${MACOS_MAJOR}" -lt "11" ] && [ "${MACOS_MINOR}" -lt "15" ]; then
-        if [ ! -d /usr/local/opt/curl ]; then
-            step "Installing Homebrew curl.."
-            brew install curl
-        fi
-        export CURLCMD="/usr/local/opt/curl/bin/curl"
-    else
-        export CURLCMD="curl"
-    fi
-}
-
-check_ccache() {
-    export PATH="/usr/local/opt/ccache/libexec:${PATH}"
-    CCACHE_STATUS=$(ccache -s >/dev/null 2>&1 && /bin/echo "CCache available." || /bin/echo "CCache is not available.")
-    info "${CCACHE_STATUS}"
-}
-
-install_obs-deps() {
-    hr "Setting up pre-built macOS OBS dependencies v${1}"
-    ensure_dir "${DEPS_BUILD_DIR}"
-    step "Download..."
-    ${CURLCMD} --progress-bar -L -C - -O https://github.com/obsproject/obs-deps/releases/download/${1}/macos-deps-${1}-${CURRENT_ARCH}.tar.xz
-    step "Unpack..."
-    mkdir -p /tmp/obsdeps
-    /usr/bin/tar -xf "./macos-deps-${1}-${CURRENT_ARCH}.tar.xz" -C /tmp/obsdeps
-    /usr/bin/xattr -r -d com.apple.quarantine /tmp/obsdeps
-}
-
-install_qt-deps() {
-    hr "Setting up pre-built dependency QT v${1}"
-    ensure_dir "${DEPS_BUILD_DIR}"
-    step "Download..."
-    ${CURLCMD} --progress-bar -L -C - -O https://github.com/obsproject/obs-deps/releases/download/${2}/macos-deps-qt-${2}-${CURRENT_ARCH}.tar.xz
-    step "Unpack..."
-    mkdir -p /tmp/obsdeps
-    /usr/bin/tar -xf ./macos-deps-qt-${2}-${CURRENT_ARCH}.tar.xz -C /tmp/obsdeps
-    /usr/bin/xattr -r -d com.apple.quarantine /tmp/obsdeps
-}
-
-install_vlc() {
-    hr "Setting up dependency VLC v${1}"
-    ensure_dir "${DEPS_BUILD_DIR}"
-    step "Download..."
-    ${CURLCMD} --progress-bar -L -C - -O https://downloads.videolan.org/vlc/${1}/vlc-${1}.tar.xz
-    step "Unpack ..."
-    /usr/bin/tar -xf vlc-${1}.tar.xz
-}
-
-install_sparkle() {
-    hr "Setting up dependency Sparkle v${1} (might prompt for password)"
-    ensure_dir "${DEPS_BUILD_DIR}/sparkle"
-    step "Download..."
-    ${CURLCMD} --progress-bar -L -C - -o sparkle.tar.bz2 https://github.com/sparkle-project/Sparkle/releases/download/${1}/Sparkle-${1}.tar.bz2
-    step "Unpack..."
-    /usr/bin/tar -xf ./sparkle.tar.bz2
-    step "Copy to destination..."
-    if [ -d /Library/Frameworks/Sparkle.framework/ ]; then
-        info "Warning - Sparkle framework already found in /Library/Frameworks"
-    else
-        sudo /bin/cp -R ./Sparkle.framework/ /Library/Frameworks/Sparkle.framework/
-    fi
-}
-
-install_cef() {
-    hr "Building dependency CEF v${1}"
-    ensure_dir "${DEPS_BUILD_DIR}"
-    step "Download..."
-    ${CURLCMD} --progress-bar -L -C - -O https://cdn-fastly.obsproject.com/downloads/cef_binary_${1}_macos_x86_64.tar.xz
-    step "Unpack..."
-    /usr/bin/tar -xf ./cef_binary_${1}_macos_x86_64.tar.xz
-    cd ./cef_binary_${1}_macos_x86_64
-    step "Fix tests..."
-    /usr/bin/sed -i '.orig' '/add_subdirectory(tests\/ceftests)/d' ./CMakeLists.txt
-    /usr/bin/sed -i '.orig' 's/"'$(test "${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}" -le 3770 && echo "10.9" || echo "10.10")'"/"'${MIN_MACOS_VERSION:-${CI_MIN_MACOS_VERSION}}'"/' ./cmake/cef_variables.cmake
-    ensure_dir ./build
-    step "Run CMAKE..."
-    cmake \
-        -DCMAKE_CXX_FLAGS="-std=c++11 -stdlib=libc++ -Wno-deprecated-declarations"\
-        -DCMAKE_EXE_LINKER_FLAGS="-std=c++11 -stdlib=libc++"\
-        -DCMAKE_OSX_DEPLOYMENT_TARGET=${MIN_MACOS_VERSION:-${CI_MIN_MACOS_VERSION}} \
-        ..
-    step "Build..."
-    /usr/bin/make -j${NPROC}
-    if [ ! -d libcef_dll ]; then /bin/mkdir libcef_dll; fi
-}
-
-## CHECK AND INSTALL PACKAGING DEPENDENCIES ##
-install_dmgbuild() {
-    if ! exists dmgbuild; then
-        if exists "pip3"; then
-            PIPCMD="pip3"
-        elif exists "pip"; then
-            PIPCMD="pip"
-        else
-            error "Pip not found - please install pip via 'python -m ensurepip'"
-            exit 1
-        fi
-
-        ${PIPCMD} install dmgbuild
-    fi
-}
-
-## OBS BUILD FROM SOURCE ##
-configure_obs_build() {
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-
-    CUR_DATE=$(/bin/date +"%Y-%m-%d@%H%M%S")
-    NIGHTLY_DIR="${CHECKOUT_DIR}/nightly-${CUR_DATE}"
-    PACKAGE_NAME=$(/usr/bin/find . -name "*.dmg")
-
-    if [ -d ./OBS.app ]; then
-        ensure_dir "${NIGHTLY_DIR}"
-        /bin/mv "../${BUILD_DIR}/OBS.app" .
-        info "You can find OBS.app in ${NIGHTLY_DIR}"
-    fi
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-    if ([ -n "${PACKAGE_NAME}" ] && [ -f ${PACKAGE_NAME} ]); then
-        ensure_dir "${NIGHTLY_DIR}"
-        /bin/mv "../${BUILD_DIR}/$(basename "${PACKAGE_NAME}")" .
-        info "You can find ${PACKAGE_NAME} in ${NIGHTLY_DIR}"
-    fi
-
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-
-    hr "Run CMAKE for OBS..."
-    cmake -DENABLE_SPARKLE_UPDATER=ON \
-        -DCMAKE_OSX_DEPLOYMENT_TARGET=${MIN_MACOS_VERSION:-${CI_MIN_MACOS_VERSION}} \
-        -DQTDIR="/tmp/obsdeps" \
-        -DSWIGDIR="/tmp/obsdeps" \
-        -DDepsPath="/tmp/obsdeps" \
-        -DVLCPath="${DEPS_BUILD_DIR}/vlc-${VLC_VERSION:-${CI_VLC_VERSION}}" \
-        -DBUILD_BROWSER=ON \
-        -DBROWSER_LEGACY="$(test "${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}" -le 3770 && echo "ON" || echo "OFF")" \
-        -DWITH_RTMPS=ON \
-        -DCEF_ROOT_DIR="${DEPS_BUILD_DIR}/cef_binary_${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}_macos_x86_64" \
-        -DCMAKE_BUILD_TYPE="${BUILD_CONFIG}" \
-        ..
-
-}
-
-run_obs_build() {
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-    hr "Build OBS..."
-    /usr/bin/make -j${NPROC}
-}
-
-## OBS BUNDLE AS MACOS APPLICATION ##
-bundle_dylibs() {
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-
-    if [ ! -d ./OBS.app ]; then
-        error "No OBS.app bundle found"
-        exit 1
-    fi
-
-    hr "Bundle dylibs for macOS application"
-
-    step "Run dylibBundler.."
-
-    BUNDLE_PLUGINS=(
-        ./OBS.app/Contents/PlugIns/coreaudio-encoder.so
-        ./OBS.app/Contents/PlugIns/decklink-ouput-ui.so
-        ./OBS.app/Contents/PlugIns/decklink-captions.so
-        ./OBS.app/Contents/PlugIns/frontend-tools.so
-        ./OBS.app/Contents/PlugIns/image-source.so
-        ./OBS.app/Contents/PlugIns/mac-avcapture.so
-        ./OBS.app/Contents/PlugIns/mac-capture.so
-        ./OBS.app/Contents/PlugIns/mac-decklink.so
-        ./OBS.app/Contents/PlugIns/mac-syphon.so
-        ./OBS.app/Contents/PlugIns/mac-vth264.so
-        ./OBS.app/Contents/PlugIns/mac-virtualcam.so
-        ./OBS.app/Contents/PlugIns/obs-browser.so
-        ./OBS.app/Contents/PlugIns/obs-ffmpeg.so
-        ./OBS.app/Contents/PlugIns/obs-filters.so
-        ./OBS.app/Contents/PlugIns/obs-transitions.so
-        ./OBS.app/Contents/PlugIns/obs-vst.so
-        ./OBS.app/Contents/PlugIns/rtmp-services.so
-        ./OBS.app/Contents/MacOS/obs-ffmpeg-mux
-        ./OBS.app/Contents/MacOS/obslua.so
-        ./OBS.app/Contents/MacOS/_obspython.so
-        ./OBS.app/Contents/PlugIns/obs-x264.so
-        ./OBS.app/Contents/PlugIns/text-freetype2.so
-        ./OBS.app/Contents/PlugIns/obs-outputs.so
-        ./OBS.app/Contents/PlugIns/aja.so
-        ./OBS.app/Contents/PlugIns/aja-output-ui.so
-        )
-
-    SEARCH_PATHS=(
-        /tmp/obsdeps/lib
-        /tmp/obsdeps/lib/QtSvg.framework
-        /tmp/obsdeps/lib/QtXml.framework
-        /tmp/obsdeps/lib/QtNetwork.framework
-        /tmp/obsdeps/lib/QtCore.framework
-        /tmp/obsdeps/lib/QtGui.framework
-        /tmp/obsdeps/lib/QtWidgets.framework
-        /tmp/obsdeps/lib/QtDBus.framework
-        /tmp/obsdeps/lib/QtPrintSupport.framework
-    )
-    if ! [ "${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}" -le 3770 ]; then
-        "${CI_SCRIPTS}/app/dylibbundler" -cd -of -a ./OBS.app -q -f \
-            -s ./OBS.app/Contents/MacOS \
-            -s "${DEPS_BUILD_DIR}/sparkle/Sparkle.framework" \
-            -s ./rundir/${BUILD_CONFIG}/bin/ \
-            $(echo "${SEARCH_PATHS[@]/#/-s }") \
-            $(echo "${BUNDLE_PLUGINS[@]/#/-x }")
-    else
-        "${CI_SCRIPTS}/app/dylibbundler" -cd -of -a ./OBS.app -q -f \
-            -s ./OBS.app/Contents/MacOS \
-            -s "${DEPS_BUILD_DIR}/sparkle/Sparkle.framework" \
-            -s ./rundir/${BUILD_CONFIG}/bin/ \
-            $(echo "${SEARCH_PATHS[@]/#/-s }") \
-            $(echo "${BUNDLE_PLUGINS[@]/#/-x }") \
-            -x ./OBS.app/Contents/PlugIns/obs-browser-page
-    fi
-
-    step "Move libobs-opengl to final destination"
-    if [ -f "./libobs-opengl/libobs-opengl.so" ]; then
-        /bin/cp ./libobs-opengl/libobs-opengl.so ./OBS.app/Contents/Frameworks
-    else
-        /bin/cp ./libobs-opengl/${BUILD_CONFIG}/libobs-opengl.so ./OBS.app/Contents/Frameworks
-    fi
-}
-
-install_frameworks() {
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-
-    if [ ! -d ./OBS.app ]; then
-        error "No OBS.app bundle found"
-        exit 1
-    fi
-
-    hr "Adding Chromium Embedded Framework"
-    step "Copy Framework..."
-    /bin/cp -R "${DEPS_BUILD_DIR}/cef_binary_${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}_macos_x86_64/Release/Chromium Embedded Framework.framework" ./OBS.app/Contents/Frameworks/
-}
-
-prepare_macos_bundle() {
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-
-    if [ ! -d ./rundir/${BUILD_CONFIG}/bin ]; then
-        error "No OBS build found"
-        return
-    fi
-
-    if [ -d ./OBS.app ]; then /bin/rm -rf ./OBS.app; fi
-
-    hr "Preparing OBS.app bundle"
-    step "Copy binary and plugins..."
-    /bin/mkdir -p OBS.app/Contents/MacOS
-    /bin/mkdir OBS.app/Contents/PlugIns
-    /bin/mkdir OBS.app/Contents/Resources
-    /bin/mkdir OBS.app/Contents/Frameworks
-
-    /bin/cp rundir/${BUILD_CONFIG}/bin/obs ./OBS.app/Contents/MacOS
-    /bin/cp rundir/${BUILD_CONFIG}/bin/obs-ffmpeg-mux ./OBS.app/Contents/MacOS
-    /bin/cp rundir/${BUILD_CONFIG}/bin/libobsglad.0.dylib ./OBS.app/Contents/MacOS
-    if ! [ "${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}" -le 3770 ]; then
-        /bin/cp -R "rundir/${BUILD_CONFIG}/bin/OBS Helper.app" "./OBS.app/Contents/Frameworks/OBS Helper.app"
-        /bin/cp -R "rundir/${BUILD_CONFIG}/bin/OBS Helper (GPU).app" "./OBS.app/Contents/Frameworks/OBS Helper (GPU).app"
-        /bin/cp -R "rundir/${BUILD_CONFIG}/bin/OBS Helper (Plugin).app" "./OBS.app/Contents/Frameworks/OBS Helper (Plugin).app"
-        /bin/cp -R "rundir/${BUILD_CONFIG}/bin/OBS Helper (Renderer).app" "./OBS.app/Contents/Frameworks/OBS Helper (Renderer).app"
-    fi
-    /bin/cp -R rundir/${BUILD_CONFIG}/data ./OBS.app/Contents/Resources
-    /bin/cp "${CI_SCRIPTS}/app/AppIcon.icns" ./OBS.app/Contents/Resources
-    /bin/cp -R rundir/${BUILD_CONFIG}/obs-plugins/ ./OBS.app/Contents/PlugIns
-    /bin/cp "${CI_SCRIPTS}/app/Info.plist" ./OBS.app/Contents
-    # Scripting plugins are required to be placed in same directory as binary
-    if [ -d ./OBS.app/Contents/Resources/data/obs-scripting ]; then
-        /bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/obslua.so ./OBS.app/Contents/MacOS/
-        /bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/_obspython.so ./OBS.app/Contents/MacOS/
-        /bin/mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/MacOS/
-        /bin/rm -rf ./OBS.app/Contents/Resources/data/obs-scripting/
-    fi
-    # dylibbundler will only copy actually linked files into bundle, but not symlinks
-    /bin/cp -cpR /tmp/obsdeps/lib/*.dylib ./OBS.app/Contents/Frameworks
-
-    bundle_dylibs
-    install_frameworks
-
-    /bin/cp "${CI_SCRIPTS}/app/OBSPublicDSAKey.pem" ./OBS.app/Contents/Resources
-
-    step "Set bundle meta information..."
-    /usr/bin/plutil -insert CFBundleVersion -string ${GIT_TAG}-${GIT_HASH} ./OBS.app/Contents/Info.plist
-    /usr/bin/plutil -insert CFBundleShortVersionString -string ${GIT_TAG}-${GIT_HASH} ./OBS.app/Contents/Info.plist
-    /usr/bin/plutil -insert OBSFeedsURL -string https://obsproject.com/osx_update/feeds.xml ./OBS.app/Contents/Info.plist
-    /usr/bin/plutil -insert SUFeedURL -string https://obsproject.com/osx_update/stable/updates.xml ./OBS.app/Contents/Info.plist
-    /usr/bin/plutil -insert SUPublicDSAKeyFile -string OBSPublicDSAKey.pem ./OBS.app/Contents/Info.plist
-}
-
-## CREATE MACOS DISTRIBUTION AND INSTALLER IMAGE ##
-prepare_macos_image() {
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-
-    if [ ! -d ./OBS.app ]; then
-        error "No OBS.app bundle found"
-        return
-    fi
-
-    hr "Preparing macOS installation image"
-
-    if [ -f "${FILE_NAME}" ]; then
-        /bin/rm "${FILE_NAME}"
-    fi
-
-    step "Run dmgbuild..."
-    /bin/cp "${CI_SCRIPTS}/package/settings.json.template" ./settings.json
-    /usr/bin/sed -i '' 's#\$\$VERSION\$\$#'"${GIT_TAG}"'#g' ./settings.json
-    /usr/bin/sed -i '' 's#\$\$CI_PATH\$\$#'"${CI_SCRIPTS}"'#g' ./settings.json
-    /usr/bin/sed -i '' 's#\$\$BUNDLE_PATH\$\$#'"${CHECKOUT_DIR}"'/build#g' ./settings.json
-    /bin/echo -n "${COLOR_ORANGE}"
-    dmgbuild "OBS-Studio ${GIT_TAG}" "${FILE_NAME}" -s ./settings.json
-    /bin/echo -n "${COLOR_RESET}"
-
-    if [ -n "${CODESIGN_OBS}" ]; then
-        codesign_image
-    fi
-}
-
-## SET UP CODE SIGNING AND NOTARIZATION CREDENTIALS ##
-##############################################################################
-# Apple Developer Identity needed:
-#
-#    + Signing the code requires a developer identity in the system's keychain
-#    + codesign will look up and find the identity automatically
-#
-##############################################################################
-read_codesign_ident() {
-    if [ ! -n "${CODESIGN_IDENT}" ]; then
-        step "Code-signing Setup"
-        /usr/bin/read -p "${COLOR_ORANGE}  + Apple developer identity: ${COLOR_RESET}" CODESIGN_IDENT
-    fi
-}
-
-##############################################################################
-# Apple Developer credentials necessary:
-#
-#   + Signing for distribution and notarization require an active Apple
-#     Developer membership
-#   + An Apple Development identity is needed for code signing
-#     (i.e. 'Apple Development: YOUR APPLE ID (PROVIDER)')
-#   + Your Apple developer ID is needed for notarization
-#   + An app-specific password is necessary for notarization from CLI
-#   + This password will be stored in your macOS keychain under the identifier
-#     'OBS-Codesign-Password'with access Apple's 'altool' only.
-##############################################################################
-
-read_codesign_pass() {
-    if [ ! -n "${CODESIGN_IDENT_PASS}" ]; then
-        step "Notarization Setup"
-        /usr/bin/read -p "${COLOR_ORANGE}  + Apple account id: ${COLOR_RESET}" CODESIGN_IDENT_USER
-        CODESIGN_IDENT_PASS=$(stty -echo; /usr/bin/read -p "${COLOR_ORANGE}  + Apple developer password: ${COLOR_RESET}" pwd; stty echo; /bin/echo $pwd)
-        /bin/echo -n "${COLOR_ORANGE}"
-        /usr/bin/xcrun altool --store-password-in-keychain-item "OBS-Codesign-Password" -u "${CODESIGN_IDENT_USER}" -p "${CODESIGN_IDENT_PASS}"
-        /bin/echo -n "${COLOR_RESET}"
-        CODESIGN_IDENT_SHORT=$(/bin/echo "${CODESIGN_IDENT}" | /usr/bin/sed -En "s/.+\((.+)\)/\1/p")
-    fi
-}
-
-codesign_bundle() {
-    if [ ! -n "${CODESIGN_OBS}" ]; then step "Skipping application bundle code signing"; return; fi
-
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-    trap "caught_error 'code-signing app'" ERR
-
-    if [ ! -d ./OBS.app ]; then
-        error "No OBS.app bundle found"
-        return
-    fi
-
-    hr "Code-signing application bundle"
-
-    /usr/bin/xattr -crs ./OBS.app
-
-    read_codesign_ident
-    step "Code-sign Sparkle framework..."
-    /bin/echo -n "${COLOR_ORANGE}"
-    /usr/bin/codesign --force --options runtime --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop"
-    /usr/bin/codesign --force --options runtime --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate"
-    /usr/bin/codesign --force --options runtime --sign "${CODESIGN_IDENT}" --deep ./OBS.app/Contents/Frameworks/Sparkle.framework
-    /bin/echo -n "${COLOR_RESET}"
-
-    step "Code-sign CEF framework..."
-    /bin/echo -n "${COLOR_ORANGE}"
-    /usr/bin/codesign --force --options runtime --entitlements "${CI_SCRIPTS}/app/entitlements.plist" --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libEGL.dylib"
-    /usr/bin/codesign --force --options runtime --entitlements "${CI_SCRIPTS}/app/entitlements.plist" --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libswiftshader_libEGL.dylib"
-    /usr/bin/codesign --force --options runtime --entitlements "${CI_SCRIPTS}/app/entitlements.plist" --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libGLESv2.dylib"
-    /usr/bin/codesign --force --options runtime --entitlements "${CI_SCRIPTS}/app/entitlements.plist" --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libswiftshader_libGLESv2.dylib"
-    if ! [ "${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}" -le 3770 ]; then
-        /usr/bin/codesign --force --options runtime --entitlements "${CI_SCRIPTS}/app/entitlements.plist" --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libvk_swiftshader.dylib"
-    fi
-
-    /bin/echo -n "${COLOR_RESET}"
-
-    if ! [ "${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}" -le 3770 ]; then
-        step "Code-sign CEF helper apps..."
-        /bin/echo -n "${COLOR_ORANGE}"
-        /usr/bin/codesign --force --options runtime --entitlements "${CI_SCRIPTS}/helpers/helper-entitlements.plist" --sign "${CODESIGN_IDENT}" --deep "./OBS.app/Contents/Frameworks/OBS Helper.app"
-        /usr/bin/codesign --force --options runtime --entitlements "${CI_SCRIPTS}/helpers/helper-gpu-entitlements.plist" --sign "${CODESIGN_IDENT}" --deep "./OBS.app/Contents/Frameworks/OBS Helper (GPU).app"
-        /usr/bin/codesign --force --options runtime --entitlements "${CI_SCRIPTS}/helpers/helper-plugin-entitlements.plist" --sign "${CODESIGN_IDENT}" --deep "./OBS.app/Contents/Frameworks/OBS Helper (Plugin).app"
-        /usr/bin/codesign --force --options runtime --entitlements "${CI_SCRIPTS}/helpers/helper-renderer-entitlements.plist" --sign "${CODESIGN_IDENT}" --deep "./OBS.app/Contents/Frameworks/OBS Helper (Renderer).app"
-        /bin/echo -n "${COLOR_RESET}"
-    fi
-
-    step "Code-sign DAL Plugin..."
-    /bin/echo -n "${COLOR_ORANGE}"
-    /usr/bin/codesign --force --options runtime --deep --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Resources/data/obs-plugins/mac-virtualcam/obs-mac-virtualcam.plugin"
-    /bin/echo -n "${COLOR_RESET}"
-
-    step "Code-sign OBS code..."
-    /bin/echo -n "${COLOR_ORANGE}"
-    /usr/bin/codesign --force --options runtime --entitlements "${CI_SCRIPTS}/app/entitlements.plist" --sign "${CODESIGN_IDENT}" --deep ./OBS.app
-    /bin/echo -n "${COLOR_RESET}"
-
-    step "Check code-sign result..."
-    /usr/bin/codesign -dvv ./OBS.app
-}
-
-codesign_image() {
-    if [ ! -n "${CODESIGN_OBS}" ]; then step "Skipping installer image code signing"; return; fi
-
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-    trap "caught_error 'code-signing image'" ERR
-
-    if [ ! -f "${FILE_NAME}" ]; then
-        error "No OBS disk image found"
-        return
-    fi
-
-    hr "Code-signing installation image"
-
-    read_codesign_ident
-
-    step "Code-sign OBS installer image..."
-    /bin/echo -n "${COLOR_ORANGE}";
-    /usr/bin/codesign --force --sign "${CODESIGN_IDENT}" "${FILE_NAME}"
-    /bin/echo -n "${COLOR_RESET}"
-    step "Check code-sign result..."
-    /usr/bin/codesign -dvv "${FILE_NAME}"
-}
-
-## BUILD FROM SOURCE META FUNCTION ##
-full-build-macos() {
-    if [ -n "${SKIP_BUILD}" ]; then step "Skipping full build"; return; fi
-
-    if [ ! -n "${SKIP_DEPS}" ]; then
-
-        hr "Installing Homebrew dependencies"
-        install_homebrew_deps
-
-        for DEPENDENCY in "${BUILD_DEPS[@]}"; do
-            set -- ${DEPENDENCY}
-            trap "caught_error ${DEPENDENCY}" ERR
-            FUNC_NAME="install_${1}"
-            ${FUNC_NAME} ${2} ${3}
-        done
-
-        check_ccache
-        trap "caught_error 'cmake'" ERR
-    fi
-
-    configure_obs_build
-    run_obs_build
-}
-
-## BUNDLE MACOS APPLICATION META FUNCTION ##
-bundle_macos() {
-    if [ ! -n "${BUNDLE_OBS}" ]; then step "Skipping application bundle creation"; return; fi
-
-    hr "Creating macOS app bundle"
-    trap "caught_error 'bundle app'" ERR
-    ensure_dir ${CHECKOUT_DIR}
-    prepare_macos_bundle
-}
-
-## PACKAGE MACOS DISTRIBUTION IMAGE META FUNCTION ##
-package_macos() {
-    if [ ! -n "${PACKAGE_OBS}" ]; then step "Skipping installer image creation"; return; fi
-
-    hr "Creating macOS .dmg image"
-    trap "caught_error 'package app'" ERR
-
-    install_dmgbuild
-    prepare_macos_image
-}
-
-## NOTARIZATION META FUNCTION ##
-notarize_macos() {
-    if [ ! -n "${NOTARIZE_OBS}" ]; then step "Skipping macOS notarization"; return; fi;
-
-    hr "Notarizing OBS for macOS"
-    trap "caught_error 'notarizing app'" ERR
-
-    ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
-
-    if [ -f "${FILE_NAME}" ]; then
-        NOTARIZE_TARGET="${FILE_NAME}"
-        xcnotary precheck "./OBS.app"
-    elif [ -d "OBS.app" ]; then
-        NOTARIZE_TARGET="./OBS.app"
-    else
-        error "No notarization app bundle ('OBS.app') or disk image ('${FILE_NAME}') found"
-        return
-    fi
-
-    if [ "$?" -eq 0 ]; then
-        read_codesign_ident
-        read_codesign_pass
-
-        step "Run xcnotary with ${NOTARIZE_TARGET}..."
-        xcnotary notarize "${NOTARIZE_TARGET}" --developer-account "${CODESIGN_IDENT_USER}" --developer-password-keychain-item "OBS-Codesign-Password" --provider "${CODESIGN_IDENT_SHORT}"
-    fi
-}
-
-## MAIN SCRIPT FUNCTIONS ##
-print_usage() {
-    /bin/echo "full-build-macos.sh - Build helper script for OBS-Studio\n"
-    /bin/echo "Usage: ${0}\n" \
-        "-d: Skip dependency checks\n" \
-        "-b: Create macOS app bundle\n" \
-        "-c: Codesign macOS app bundle\n" \
-        "-p: Package macOS app into disk image\n" \
-        "-n: Notarize macOS app and disk image (implies -b)\n" \
-        "-s: Skip build process (useful for bundling/packaging only)\n" \
-        "-h: Print this help"
-    exit 0
-}
-
-obs-build-main() {
-    ensure_dir ${CHECKOUT_DIR}
-    check_macos_version
-    step "Fetching OBS tags..."
-    /usr/bin/git fetch origin --tags
-    GIT_BRANCH=$(/usr/bin/git rev-parse --abbrev-ref HEAD)
-    GIT_HASH=$(/usr/bin/git rev-parse --short HEAD)
-    GIT_TAG=$(/usr/bin/git describe --tags --abbrev=0)
-    FILE_NAME="obs-studio-${GIT_TAG}-${GIT_HASH}-macOS.dmg"
-
-    ##########################################################################
-    # IMPORTANT:
-    #
-    # Be careful when choosing to notarize and code-sign. The script will try
-    # to sign any pre-existing bundle but also pre-existing images.
-    #
-    # This could lead to a package containing a non-signed bundle, which
-    # will then fail notarization.
-    #
-    # To avoid this, run this script with -b -c first, then -p -c or -p -n
-    # after to make sure that a code-signed bundle will be packaged.
-    #
-    ##########################################################################
-
-    while getopts ":hdsbnpc" OPTION; do
-        case ${OPTION} in
-            h) print_usage ;;
-            d) SKIP_DEPS=1 ;;
-            s) SKIP_BUILD=1 ;;
-            b) BUNDLE_OBS=1 ;;
-            n) CODESIGN_OBS=1; NOTARIZE_OBS=1 ;;
-            p) PACKAGE_OBS=1 ;;
-            c) CODESIGN_OBS=1 ;;
-            \?) ;;
-        esac
-    done
-
-    full-build-macos
-    bundle_macos
-    codesign_bundle
-    package_macos
-    codesign_image
-    notarize_macos
-
-    cleanup
-}
-
-obs-build-main $*

+ 5 - 0
CI/include/Brewfile

@@ -0,0 +1,5 @@
+brew "cmake"
+brew "ccache"
+brew "ninja"
+brew "freetype"
+brew "cmocka"

+ 2 - 0
CI/include/Wingetfile

@@ -0,0 +1,2 @@
+package '7zip.7zip', path: '7-zip', bin: '7z'
+package 'cmake', path: 'Cmake\bin', bin: 'cmake'

+ 217 - 0
CI/include/build_support.sh

@@ -0,0 +1,217 @@
+#!/bin/bash
+
+##############################################################################
+# Unix support functions
+##############################################################################
+#
+# This script file can be included in build scripts for UNIX-compatible
+# shells to compose build scripts.
+#
+##############################################################################
+
+## DEFINE UTILITIES ##
+
+if [ -z "${QUIET}" ]; then
+    status() {
+        echo -e "${COLOR_BLUE}[${PRODUCT_NAME}] ${1}${COLOR_RESET}"
+    }
+
+    step() {
+        echo -e "${COLOR_GREEN}  + ${1}${COLOR_RESET}"
+    }
+
+    info() {
+        echo -e "${COLOR_ORANGE}  + ${1}${COLOR_RESET}"
+    }
+
+    error() {
+        echo -e "${COLOR_RED}  + ${1}${COLOR_RESET}"
+    }
+else
+    status() {
+        :
+    }
+
+    step() {
+        :
+    }
+
+    info() {
+        :
+    }
+
+    error() {
+        echo -e "${COLOR_RED}  + ${1}${COLOR_RESET}"
+    }
+fi
+
+exists() {
+  /usr/bin/command -v "$1" >/dev/null 2>&1
+}
+
+ensure_dir() {
+    [ -n "${1}" ] && /bin/mkdir -p "${1}" && builtin cd "${1}"
+}
+
+cleanup() {
+    :
+}
+
+caught_error() {
+    error "ERROR during build step: ${1}"
+    cleanup
+    exit 1
+}
+
+# Setup build environment
+BUILD_DIR="${BUILD_DIR:-build}"
+BUILD_CONFIG="${BUILD_CONFIG:-RelWithDebInfo}"
+CI_WORKFLOW="${CHECKOUT_DIR}/.github/workflows/main.yml"
+CURRENT_ARCH="$(uname -m)"
+CURRENT_DATE="$(date +"%Y-%m-%d")"
+
+## Utility functions ##
+
+check_ccache() {
+    step "Check CCache..."
+    if ccache -V >/dev/null 2>&1; then
+        info "CCache available"
+        CMAKE_CCACHE_OPTIONS="-DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache"
+
+        if [ "${CI}" ]; then
+            ccache --set-config=cache_dir=${GITHUB_WORKSPACE:-${HOME}}/.ccache
+            ccache --set-config=max_size=${CCACHE_SIZE:-500M}
+            ccache --set-config=compression=true
+            ccache -z
+        fi
+    else
+        info "CCache not available"
+    fi
+}
+
+safe_fetch() {
+    if [ $# -lt 2 ]; then
+        error "Usage: safe_fetch URL HASH"
+        return 1
+    fi
+
+    while true; do
+        case "${1}" in
+            -n | --nocontinue ) NOCONTINUE=TRUE; shift ;;
+            -- ) shift; break ;;
+            * ) break ;;
+        esac
+    done
+
+    DOWNLOAD_URL="${1}"
+    DOWNLOAD_HASH="${2}"
+    DOWNLOAD_FILE="$(basename ${DOWNLOAD_URL})"
+    CURLCMD=${CURLCMD:-curl}
+
+    if [ "${NOCONTINUE}" ]; then
+        ${CURLCMD/--continue-at -/} "${DOWNLOAD_URL}"
+    else
+        ${CURLCMD} "${DOWNLOAD_URL}"
+    fi
+
+    if [ "${DOWNLOAD_HASH}" = "$(sha256sum "${DOWNLOAD_FILE}" | cut -d " " -f 1)" ]; then
+        info "${DOWNLOAD_FILE} downloaded successfully and passed hash check"
+        return 0
+    else
+        error "${DOWNLOAD_FILE} downloaded successfully and failed hash check"
+        return 1
+    fi
+}
+
+check_and_fetch() {
+    if [ $# -lt 2 ]; then
+        caught_error "Usage: check_and_fetch URL HASH"
+    fi
+
+    while true; do
+        case "${1}" in
+            -n | --nocontinue ) NOCONTINUE=TRUE; shift ;;
+            -- ) shift; break ;;
+            * ) break ;;
+        esac
+    done
+
+    DOWNLOAD_URL="${1}"
+    DOWNLOAD_HASH="${2}"
+    DOWNLOAD_FILE="$(basename "${DOWNLOAD_URL}")"
+
+    if [ -f "${DOWNLOAD_FILE}" ] && [ "${DOWNLOAD_HASH}" = "$(sha256sum "${DOWNLOAD_FILE}" | cut -d " " -f 1)" ]; then
+        info "${DOWNLOAD_FILE} exists and passed hash check"
+        return 0
+    else
+        safe_fetch "${DOWNLOAD_URL}" "${DOWNLOAD_HASH}"
+    fi
+}
+
+github_fetch() {
+    if [ $# -ne 3 ]; then
+        error "Usage: github_fetch GITHUB_USER GITHUB_REPOSITORY GITHUB_COMMIT_HASH"
+        return 1
+    fi
+
+    GH_USER="${1}"
+    GH_REPO="${2}"
+    GH_REF="${3}"
+
+    if [ -d "./.git" ]; then
+        info "Repository ${GH_USER}/${GH_REPO} already exists, updating..."
+        git config advice.detachedHead false
+        git config remote.origin.url "https://github.com/${GH_USER}/${GH_REPO}.git"
+        git config remote.origin.fetch "+refs/heads/master:refs/remotes/origin/master"
+        git config remote.origin.tapOpt --no-tags
+
+        if ! git rev-parse -q --verify "${GH_COMMIT}^{commit}"; then
+            git fetch origin
+        fi
+
+        git checkout -f "${GH_REF}" --
+        git reset --hard "${GH_REF}" --
+        if [ -d "./.gitmodules" ]; then
+            git submodule foreach --recursive git submodule sync
+            git submodule update --init --recursive
+        fi
+
+    else
+        git clone "https://github.com/${GH_USER}/${GH_REPO}.git" "$(pwd)"
+        git config advice.detachedHead false
+        info "Checking out commit ${GH_REF}..."
+        git checkout -f "${GH_REF}" --
+
+        if [ -d "./.gitmodules" ]; then
+            git submodule foreach --recursive git submodule sync
+            git submodule update --init --recursive
+        fi
+    fi
+}
+
+apply_patch() {
+    if [ $# -ne 2 ]; then
+        error "Usage: apply_patch PATCH_URL PATCH_HASH"
+        return 1
+    fi
+
+    COMMIT_URL="${1}"
+    COMMIT_HASH="${2}"
+    PATCH_FILE="$(basename ${COMMIT_URL})"
+
+    if [ "${COMMIT_URL:0:5}" = "https" ]; then
+        ${CURLCMD:-curl} "${COMMIT_URL}"
+        if [ "${COMMIT_HASH}" = "$(sha256sum ${PATCH_FILE} | cut -d " " -f 1)" ]; then
+            info "${PATCH_FILE} downloaded successfully and passed hash check"
+        else
+            error "${PATCH_FILE} downloaded successfully and failed hash check"
+            return 1
+        fi
+
+        info "Applying patch ${COMMIT_URL}"
+    else
+        PATCH_FILE="${COMMIT_URL}"
+    fi
+
+    patch -g 0 -f -p1 -i "${PATCH_FILE}"
+}

+ 42 - 0
CI/include/build_support_freebsd.sh

@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+##############################################################################
+# FreeBSD support functions
+##############################################################################
+#
+# This script file can be included in build scripts for FreeBSD.
+#
+##############################################################################
+
+# Setup build environment
+
+if [ "${TERM-}" -a -x /usr/local/bin/tput ]; then
+    COLOR_RED=$(/usr/local/bin/tput setaf 1)
+    COLOR_GREEN=$(/usr/local/bin/tput setaf 2)
+    COLOR_BLUE=$(/usr/local/bin/tput setaf 4)
+    COLOR_ORANGE=$(/usr/local/bin/tput setaf 3)
+    COLOR_RESET=$(/usr/local/bin/tput sgr0)
+else
+    COLOR_RED=""
+    COLOR_GREEN=""
+    COLOR_BLUE=""
+    COLOR_ORANGE=""
+    COLOR_RESET=""
+fi
+
+if [ "${CI}" -o "${QUIET}" ]; then
+    export CURLCMD="curl --silent --show-error --location -O"
+else
+    export CURLCMD="curl --progress-bar --location --continue-at - -O"
+fi
+
+_add_ccache_to_path() {
+    if [ "${CMAKE_CCACHE_OPTIONS}" ]; then
+        PATH="/usr/local/opt/ccache/libexec:${PATH}"
+        status "Compiler Info:"
+        local IFS=$'\n'
+        for COMPILER_INFO in $(type cc c++ gcc g++ clang clang++ || true); do
+            info "${COMPILER_INFO}"
+        done
+    fi
+}

+ 44 - 0
CI/include/build_support_linux.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+
+##############################################################################
+# Linux support functions
+##############################################################################
+#
+# This script file can be included in build scripts for Linux.
+#
+##############################################################################
+
+# Setup build environment
+
+CI_LINUX_CEF_VERSION=$(cat "${CI_WORKFLOW}" | sed -En "s/[ ]+CEF_BUILD_VERSION_LINUX: '([0-9]+)'/\1/p")
+
+if [ "${TERM-}" -a -z "${CI}" ]; then
+    COLOR_RED=$(tput setaf 1)
+    COLOR_GREEN=$(tput setaf 2)
+    COLOR_BLUE=$(tput setaf 4)
+    COLOR_ORANGE=$(tput setaf 3)
+    COLOR_RESET=$(tput sgr0)
+else
+    COLOR_RED=""
+    COLOR_GREEN=""
+    COLOR_BLUE=""
+    COLOR_ORANGE=""
+    COLOR_RESET=""
+fi
+
+if [ "${CI}" -o "${QUIET}" ]; then
+    export CURLCMD="curl --silent --show-error --location -O"
+else
+    export CURLCMD="curl --progress-bar --location --continue-at - -O"
+fi
+
+_add_ccache_to_path() {
+    if [ "${CMAKE_CCACHE_OPTIONS}" ]; then
+        PATH="/usr/local/opt/ccache/libexec:${PATH}"
+        status "Compiler Info:"
+        local IFS=$'\n'
+        for COMPILER_INFO in $(type cc c++ gcc g++ clang clang++ || true); do
+            info "${COMPILER_INFO}"
+        done
+    fi
+}

+ 156 - 0
CI/include/build_support_macos.sh

@@ -0,0 +1,156 @@
+#!/bin/bash
+
+##############################################################################
+# macOS support functions
+##############################################################################
+#
+# This script file can be included in build scripts for macOS.
+#
+##############################################################################
+
+# Setup build environment
+WORKFLOW_CONTENT=$(/bin/cat "${CI_WORKFLOW}")
+CI_DEPS_VERSION=$(echo "${WORKFLOW_CONTENT}" | /usr/bin/sed -En "s/[ ]+DEPS_VERSION_MAC: '([0-9\-]+)'/\1/p")
+CI_VLC_VERSION=$(echo "${WORKFLOW_CONTENT}" | /usr/bin/sed -En "s/[ ]+VLC_VERSION_MAC: '([0-9\.]+)'/\1/p")
+CI_SPARKLE_VERSION=$(echo "${WORKFLOW_CONTENT}" | /usr/bin/sed -En "s/[ ]+SPARKLE_VERSION: '([0-9\.]+)'/\1/p")
+CI_QT_VERSION=$(echo "${WORKFLOW_CONTENT}" | /usr/bin/sed -En "s/[ ]+QT_VERSION_MAC: '([0-9\.]+)'/\1/p" | /usr/bin/head -1)
+CI_MACOSX_DEPLOYMENT_TARGET=$(echo "${WORKFLOW_CONTENT}" | /usr/bin/sed -En "s/[ ]+MACOSX_DEPLOYMENT_TARGET: '([0-9\.]+)'/\1/p")
+CI_MACOS_CEF_VERSION=$(echo "${WORKFLOW_CONTENT}" | /usr/bin/sed -En "s/[ ]+CEF_BUILD_VERSION_MAC: '([0-9]+)'/\1/p")
+
+MACOS_VERSION="$(/usr/bin/sw_vers -productVersion)"
+MACOS_MAJOR="$(echo ${MACOS_VERSION} | /usr/bin/cut -d '.' -f 1)"
+MACOS_MINOR="$(echo ${MACOS_VERSION} | /usr/bin/cut -d '.' -f 2)"
+
+if [ "${TERM-}" -a -z "${CI}" ]; then
+    COLOR_RED=$(/usr/bin/tput setaf 1)
+    COLOR_GREEN=$(/usr/bin/tput setaf 2)
+    COLOR_BLUE=$(/usr/bin/tput setaf 4)
+    COLOR_ORANGE=$(/usr/bin/tput setaf 3)
+    COLOR_RESET=$(/usr/bin/tput sgr0)
+else
+    COLOR_RED=""
+    COLOR_GREEN=""
+    COLOR_BLUE=""
+    COLOR_ORANGE=""
+    COLOR_RESET=""
+fi
+
+## DEFINE UTILITIES ##
+check_macos_version() {
+    step "Check macOS version..."
+    MIN_VERSION=${MACOSX_DEPLOYMENT_TARGET:-${CI_MACOSX_DEPLOYMENT_TARGET}}
+    MIN_MAJOR=$(echo ${MIN_VERSION} | /usr/bin/cut -d '.' -f 1)
+    MIN_MINOR=$(echo ${MIN_VERSION} | /usr/bin/cut -d '.' -f 2)
+
+    if [ "${MACOS_MAJOR}" -lt "11" -a "${MACOS_MINOR}" -lt "${MIN_MINOR}" ]; then
+        error "ERROR: Minimum required macOS version is ${MIN_VERSION}, but running on ${MACOS_VERSION}"
+    fi
+
+    if [ "${MACOS_MAJOR}" -ge "11" ]; then
+        export CODESIGN_LINKER="ON"
+    fi
+}
+
+install_homebrew_deps() {
+    if ! exists brew; then
+        caught_error "Homebrew not found - please install Homebrew (https://brew.sh)"
+    fi
+
+    brew bundle --file "${CHECKOUT_DIR}/CI/include/Brewfile" ${QUIET:+--quiet}
+
+    check_curl
+}
+
+check_curl() {
+    if [ "${MACOS_MAJOR}" -lt "11" -a "${MACOS_MINOR}" -lt "15" ]; then
+        if [ ! -d /usr/local/opt/curl ]; then
+            step "Install Homebrew curl..."
+            brew install curl
+        fi
+
+        CURLCMD="/usr/local/opt/curl/bin/curl"
+    else
+        CURLCMD="curl"
+    fi
+
+    if [ "${CI}" -o "${QUIET}" ]; then
+        export CURLCMD="${CURLCMD} --silent --show-error --location -O"
+    else
+        export CURLCMD="${CURLCMD} --progress-bar --location --continue-at - -O"
+    fi
+}
+
+check_archs() {
+    step "Check Architecture..."
+    ARCH="${ARCH:-${CURRENT_ARCH}}"
+    if [ "${ARCH}" = "universal" ]; then
+        CMAKE_ARCHS="x86_64;arm64"
+    elif [ "${ARCH}" != "x86_64" -a "${ARCH}" != "arm64" ]; then
+        caught_error "Unsupported architecture '${ARCH}' provided"
+    else
+        CMAKE_ARCHS="${ARCH}"
+    fi
+}
+
+_add_ccache_to_path() {
+    if [ "${CMAKE_CCACHE_OPTIONS}" ]; then
+        if [ "${CURRENT_ARCH}" == "arm64" ]; then
+            PATH="/opt/homebrew/opt/ccache/libexec:${PATH}"
+        else
+            PATH="/usr/local/opt/ccache/libexec:${PATH}"
+        fi
+        status "Compiler Info:"
+        local IFS=$'\n'
+        for COMPILER_INFO in $(type cc c++ gcc g++ clang clang++ || true); do
+            info "${COMPILER_INFO}"
+        done
+    fi
+}
+
+## SET UP CODE SIGNING AND NOTARIZATION CREDENTIALS ##
+##############################################################################
+# Apple Developer Identity needed:
+#
+#    + Signing the code requires a developer identity in the system's keychain
+#    + codesign will look up and find the identity automatically
+#
+##############################################################################
+read_codesign_ident() {
+    if [ -z "${CODESIGN_IDENT}" ]; then
+        step "Set up code signing..."
+        read -p "${COLOR_ORANGE}  + Apple developer identity: ${COLOR_RESET}" CODESIGN_IDENT
+    fi
+}
+
+##############################################################################
+# Apple Developer credentials necessary:
+#
+#   + Signing for distribution and notarization require an active Apple
+#     Developer membership
+#   + An Apple Development identity is needed for code signing
+#     (i.e. 'Apple Development: YOUR APPLE ID (PROVIDER)')
+#   + Your Apple developer ID is needed for notarization
+#   + An app-specific password is necessary for notarization from CLI
+#   + This password will be stored in your macOS keychain under the identifier
+#     'OBS-Codesign-Password' with access Apple's 'altool' only.
+##############################################################################
+
+read_codesign_pass() {
+    step "Set up notarization..."
+
+    if [ -z "${CODESIGN_IDENT_USER}" ]; then
+        read -p "${COLOR_ORANGE}  + Apple account id: ${COLOR_RESET}" CODESIGN_IDENT_USER
+    fi
+
+    if [ -z "${CODESIGN_IDENT_PASS}" ]; then
+        CODESIGN_IDENT_PASS=$(stty -echo; read -p "${COLOR_ORANGE}  + Apple developer password: ${COLOR_RESET}" secret; stty echo; echo $secret)
+        echo ""
+    fi
+
+    step "Update notarization keychain..."
+
+    echo -n "${COLOR_ORANGE}"
+    /usr/bin/xcrun altool --store-password-in-keychain-item "OBS-Codesign-Password" -u "${CODESIGN_IDENT_USER}" -p "${CODESIGN_IDENT_PASS}"
+    echo -n "${COLOR_RESET}"
+    CODESIGN_IDENT_SHORT=$(echo "${CODESIGN_IDENT}" | /usr/bin/sed -En "s/.+\((.+)\)/\1/p")
+}

+ 156 - 0
CI/include/build_support_windows.ps1

@@ -0,0 +1,156 @@
+$CIWorkflow = "${CheckoutDir}/.github/workflows/main.yml"
+$WorkflowContent = Get-Content ${CIWorkflow}
+
+$CIDepsVersion = ${WorkflowContent} | Select-String "[ ]+DEPS_VERSION_WIN: '([0-9\-]+)'" | ForEach-Object{$_.Matches.Groups[1].Value}
+$CIQtVersion = ${WorkflowContent} | Select-String "[ ]+QT_VERSION_WIN: '([0-9\.]+)'" | ForEach-Object{$_.Matches.Groups[1].Value}
+$CIVlcVersion = ${WorkflowContent} | Select-String "[ ]+VLC_VERSION_WIN: '(.+)'" | ForEach-Object{$_.Matches.Groups[1].Value}
+$CICefVersion = ${WorkflowContent} | Select-String "[ ]+CEF_BUILD_VERSION_WIN: '([0-9\.]+)'" | ForEach-Object{$_.Matches.Groups[1].Value}
+$CIGenerator = ${WorkflowContent} | Select-String "[ ]+CMAKE_GENERATOR: '(.+)'" | ForEach-Object{$_.Matches.Groups[1].Value}
+
+function Write-Status {
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String] $Output
+    )
+
+    if (!($Quiet.isPresent)) {
+        if (Test-Path Env:CI) {
+            Write-Host "[${ProductName}] ${Output}"
+        } else {
+            Write-Host -ForegroundColor blue "[${ProductName}] ${Output}"
+        }
+    }
+}
+
+function Write-Info {
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String] $Output
+    )
+
+    if (!($Quiet.isPresent)) {
+        if (Test-Path Env:CI) {
+            Write-Host " + ${Output}"
+        } else {
+            Write-Host -ForegroundColor DarkYellow " + ${Output}"
+        }
+    }
+}
+
+function Write-Step {
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String] $Output
+    )
+
+    if (!($Quiet.isPresent)) {
+        if (Test-Path Env:CI) {
+            Write-Host " + ${Output}"
+        } else {
+            Write-Host -ForegroundColor green " + ${Output}"
+        }
+    }
+}
+
+function Write-Failure {
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String] $Output
+    )
+
+    if (Test-Path Env:CI) {
+        Write-Host " + ${Output}"
+    } else {
+        Write-Host -ForegroundColor red " + ${Output}"
+    }
+}
+
+function Test-CommandExists {
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String] $Command
+    )
+
+    $CommandExists = $false
+    $OldActionPref = $ErrorActionPreference
+    $ErrorActionPreference = "stop"
+
+    try {
+        if (Get-Command $Command) {
+            $CommandExists = $true
+        }
+    } Catch {
+        $CommandExists = $false
+    } Finally {
+        $ErrorActionPreference = $OldActionPref
+    }
+
+    return $CommandExists
+}
+
+function Ensure-Directory {
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String] $Directory
+    )
+
+    if (!(Test-Path $Directory)) {
+        $null = New-Item -ItemType Directory -Force -Path $Directory
+    }
+
+    Set-Location -Path $Directory
+}
+
+$BuildDirectory = "$(if (Test-Path Env:BuildDirectory) { $env:BuildDirectory } else { $BuildDirectory })"
+$BuildConfiguration = "$(if (Test-Path Env:BuildConfiguration) { $env:BuildConfiguration } else { $BuildConfiguration })"
+$BuildArch = "$(if (Test-Path Env:BuildArch) { $env:BuildArch } else { $BuildArch })"
+$WindowsDepsVersion = "$(if (Test-Path Env:WindowsDepsVersion ) { $env:WindowsDepsVersion } else { $CIDepsVersion })"
+$WindowsQtVersion = "$(if (Test-Path Env:WindowsQtVersion ) { $env:WindowsQtVersion } else { $CIQtVersion })"
+$WindowsVlcVersion = "$(if (Test-Path Env:WindowsVlcVersion ) { $env:WindowsVlcVersion } else { $CIVlcVersion })"
+$WindowsCefVersion = "$(if (Test-Path Env:WindowsCefVersion ) { $env:WindowsCefVersion } else { $CICefVersion })"
+$CmakeSystemVersion = "$(if (Test-Path Env:CMAKE_SYSTEM_VERSION) { $Env:CMAKE_SYSTEM_VERSION } else { "10.0.18363.657" })"
+$CmakeGenerator = "$(if (Test-Path Env:CmakeGenerator) { $Env:CmakeGenerator } else { $CIGenerator })"
+
+
+function Install-Windows-Dependencies {
+    $WingetFile = "$PSScriptRoot/Wingetfile"
+
+    $Host64Bit = [System.Environment]::Is64BitOperatingSystem
+
+    $Prefix = (${Env:ProgramFiles(x86)}, $Env:ProgramFiles)[$Host64Bit]
+
+    $Paths = $Env:Path -split [System.IO.Path]::PathSeparator
+
+    $WingetOptions = @('install', '--accept-package-agreements', '--accept-source-agreements')
+
+    if ( $script:Quiet ) {
+        $WingetOptions += '--silent'
+    }
+
+    Get-Content $WingetFile | ForEach-Object {
+        $_, $Package, $_, $Path, $_, $Binary = $_ -replace ',','' -replace "'", '' -split ' '
+
+        $FullPath = "${Prefix}\${Path}"
+        if ( ( Test-Path $FullPath  ) -and ! ( $Paths -contains $FullPath ) ) {
+            $Paths += $FullPath
+            $Env:Path = $Paths -join [System.IO.Path]::PathSeparator
+        }
+
+        Write-Step "Checking for command ${Binary}"
+        $Found = Get-Command -ErrorAction SilentlyContinue $Binary
+
+        if ( $Found ) {
+            Write-Info "Found dependency ${Binary} as $($Found.Source)"
+        } else {
+            Write-Info "Installing package ${Package}"
+
+            try {
+                $Params = $WingetOptions + $Package
+
+                winget @Params
+            } catch {
+                throw "Error while installing winget package ${Package}: $_"
+            }
+        }
+    }
+}

+ 0 - 52
CI/install-dependencies-linux.sh

@@ -1,52 +0,0 @@
-#!/bin/sh
-set -ex
-
-curl -L https://packagecloud.io/github/git-lfs/gpgkey | sudo apt-key add -
-
-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 \
-        libxkbcommon-dev \
-        libxcb-randr0-dev \
-        libxcb-shm0-dev \
-        libxcb-xinerama0-dev \
-        libxcomposite-dev \
-        libxinerama-dev \
-        libmbedtls-dev \
-        pkg-config \
-        python3-dev \
-        qtbase5-dev \
-        qtbase5-private-dev \
-        libqt5svg5-dev \
-        swig
-
-# build cef
-wget --quiet --retry-connrefused --waitretry=1 https://cdn-fastly.obsproject.com/downloads/cef_binary_${LINUX_CEF_BUILD_VERSION}_linux64.tar.bz2
-tar -xjf ./cef_binary_${LINUX_CEF_BUILD_VERSION}_linux64.tar.bz2

+ 0 - 4
CI/install-qt-win.cmd

@@ -1,4 +0,0 @@
-if exist Qt_5.15.2.7z (curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_5.15.2.7z -f --retry 5 -z Qt_5.15.2.7z) else (curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_5.15.2.7z -f --retry 5 -C -)
-7z x Qt_5.15.2.7z -oQt
-mv Qt C:\QtDep
-dir C:\QtDep

+ 0 - 4
CI/install-script-linux.sh

@@ -1,4 +0,0 @@
-#!/bin/sh
-set -ex
-
-build_config=RelWithDebInfo

+ 0 - 30
CI/install-script-win.cmd

@@ -1,30 +0,0 @@
-if exist dependencies2019.zip (curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies2019.zip -f --retry 5 -z dependencies2019.zip) else (curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies2019.zip -f --retry 5 -C -)
-if exist vlc.zip (curl -kLO https://cdn-fastly.obsproject.com/downloads/vlc.zip -f --retry 5 -z vlc.zip) else (curl -kLO https://cdn-fastly.obsproject.com/downloads/vlc.zip -f --retry 5 -C -)
-if exist cef_binary_%CEF_VERSION%_windows32_minimal.zip (curl -kLO https://cdn-fastly.obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows32_minimal.zip -f --retry 5 -z cef_binary_%CEF_VERSION%_windows32_minimal.zip) else (curl -kLO https://cdn-fastly.obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows32_minimal.zip -f --retry 5 -C -)
-if exist cef_binary_%CEF_VERSION%_windows64_minimal.zip (curl -kLO https://cdn-fastly.obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows64_minimal.zip -f --retry 5 -z cef_binary_%CEF_VERSION%_windows64_minimal.zip) else (curl -kLO https://cdn-fastly.obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows64_minimal.zip -f --retry 5 -C -)
-7z x dependencies2019.zip -odependencies2019
-7z x vlc.zip -ovlc
-7z x cef_binary_%CEF_VERSION%_windows32_minimal.zip -oCEF_32
-7z x cef_binary_%CEF_VERSION%_windows64_minimal.zip -oCEF_64
-set DepsPath32=%CD%\dependencies2019\win32
-set DepsPath64=%CD%\dependencies2019\win64
-set VLCPath=%CD%\vlc
-set QTDIR32=C:\QtDep\5.15.2\msvc2019
-set QTDIR64=C:\QtDep\5.15.2\msvc2019_64
-set CEF_32=%CD%\CEF_32\cef_binary_%CEF_VERSION%_windows32_minimal
-set CEF_64=%CD%\CEF_64\cef_binary_%CEF_VERSION%_windows64_minimal
-set build_config=RelWithDebInfo
-set VIRTUALCAM-GUID=A3FCE0F5-3493-419F-958A-ABA1250EC20B
-mkdir build build32 build64
-if "%TWITCH-CLIENTID%"=="$(twitch_clientid)" (
-cd ./build32
-cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DENABLE_VLC=ON -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_32% -DVIRTUALCAM_GUID="%VIRTUALCAM-GUID%" ..
-cd ../build64
-cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DENABLE_VLC=ON -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_64% -DVIRTUALCAM_GUID="%VIRTUALCAM-GUID%" ..
-) else (
-cd ./build32
-cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DENABLE_VLC=ON -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_32% -DTWITCH_CLIENTID="%TWITCH-CLIENTID%" -DTWITCH_HASH="%TWITCH-HASH%" -DRESTREAM_CLIENTID="%RESTREAM-CLIENTID%" -DRESTREAM_HASH="%RESTREAM-HASH%" -DVIRTUALCAM_GUID="%VIRTUALCAM-GUID%" ..
-cd ../build64
-cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DENABLE_VLC=ON -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_64% -DTWITCH_CLIENTID="%TWITCH-CLIENTID%" -DTWITCH_HASH="%TWITCH-HASH%"  -DRESTREAM_CLIENTID="%RESTREAM-CLIENTID%" -DRESTREAM_HASH="%RESTREAM-HASH%" -DVIRTUALCAM_GUID="%VIRTUALCAM-GUID%" ..
-)
-cd ..

+ 136 - 0
CI/linux/01_install_dependencies.sh

@@ -0,0 +1,136 @@
+#!/bin/bash
+
+##############################################################################
+# Linux dependency management function
+##############################################################################
+#
+# This script file can be included in build scripts for Linux or run directly
+# with the -s/--standalone switch
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+install_build-deps() {
+    shift
+    status "Install OBS build dependencies"
+    trap "caught_error 'install_build-deps'" ERR
+
+    sudo apt-get install -y $@
+}
+
+install_obs-deps() {
+    shift
+    status "Install OBS dependencies"
+    trap "caught_error 'install_obs-deps'" ERR
+
+    if [ -z "${DISABLE_PIPEWIRE}" ]; then
+        sudo apt-get install -y $@ libpipewire-0.3-dev
+    else
+        sudo apt-get install -y $@
+    fi
+}
+
+install_qt-deps() {
+    shift
+    status "Install Qt dependencies"
+    trap "caught_error 'install_qt-deps'" ERR
+
+    sudo apt-get install -y $@
+}
+
+install_cef() {
+    shift
+    status "Setup for dependency CEF v${1}"
+    ensure_dir "${DEPS_BUILD_DIR}"
+
+    if [ "${CI}" -a "${RESTORED_CEF}" ]; then
+        _SKIP=TRUE
+    elif [ -d "${DEPS_BUILD_DIR}/cef_binary_${1}_linux64" -a -f "${DEPS_BUILD_DIR}/cef_binary_${1}_linux64/build/libcef_dll_wrapper/libcef_dll_wrapper.a" ]; then
+        _SKIP=TRUE
+    fi
+
+    if [ -z "${_SKIP}" ]; then
+        step "Download..."
+        ${CURLCMD:-curl -O} https://cdn-fastly.obsproject.com/downloads/cef_binary_${1}_linux64.tar.bz2
+        step "Unpack..."
+        tar -xf cef_binary_${1}_linux64.tar.bz2
+    else
+        step "Found existing Chromium Embedded Framework and loader library..."
+    fi
+}
+
+install_plugin-deps() {
+    shift
+    status "Install plugin dependencies"
+    trap "caught_error 'install_plugin-deps'" ERR
+
+    sudo apt-get install -y $@
+}
+
+install_dependencies() {
+    status "Set up apt"
+    trap "caught_error 'install_dependencies'" ERR
+
+    BUILD_DEPS=(
+        "build-deps cmake ninja-build pkg-config clang clang-format build-essential curl ccache"
+        "obs-deps libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev libswresample-dev \
+         libswscale-dev libx264-dev libcurl4-openssl-dev libmbedtls-dev libgl1-mesa-dev libjansson-dev \
+         libluajit-5.1-dev python3-dev libx11-dev libxcb-randr0-dev libxcb-shm0-dev libxcb-xinerama0-dev \
+         libxcomposite-dev libxinerama-dev libxcb1-dev libx11-xcb-dev libxcb-xfixes0-dev swig libcmocka-dev \
+         libpci-dev libxss-dev libglvnd-dev libgles2-mesa libgles2-mesa-dev libwayland-dev libxkbcommon-dev"
+        "qt-deps qtbase5-dev qtbase5-private-dev libqt5svg5-dev qtwayland5"
+        "cef ${LINUX_CEF_BUILD_VERSION:-${CI_LINUX_CEF_VERSION}}"
+        "plugin-deps libasound2-dev libfdk-aac-dev libfontconfig-dev libfreetype6-dev libjack-jackd2-dev \
+         libpulse-dev libsndio-dev libspeexdsp-dev libudev-dev libv4l-dev libva-dev libvlc-dev libdrm-dev"
+    )
+
+    sudo dpkg --add-architecture amd64
+    sudo apt-get -qq update
+
+    for DEPENDENCY in "${BUILD_DEPS[@]}"; do
+        set -- ${DEPENDENCY}
+        trap "caught_error ${DEPENDENCY}" ERR
+        FUNC_NAME="install_${1}"
+        ${FUNC_NAME} ${@}
+    done
+}
+
+install-dependencies-standalone() {
+    CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
+    PRODUCT_NAME="OBS-Studio"
+    DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+    source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+    source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh"
+
+    status "Setup of OBS build dependencies"
+    install_dependencies
+}
+
+print_usage() {
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "--disable-pipewire             : Disable building with Pipewire support (default: off)\n"
+}
+
+install-dependencies-main() {
+    if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
+        while true; do
+            case "${1}" in
+                -h | --help ) print_usage; exit 0 ;;
+                -q | --quiet ) export QUIET=TRUE; shift ;;
+                -v | --verbose ) export VERBOSE=TRUE; shift ;;
+                --disable-pipewire ) DISABLE_PIPEWIRE=TRUE; shift ;;
+                -- ) shift; break ;;
+                * ) break ;;
+            esac
+        done
+
+        install-dependencies-standalone
+    fi
+}
+
+install-dependencies-main $*

+ 127 - 0
CI/linux/02_build_obs.sh

@@ -0,0 +1,127 @@
+#!/bin/bash
+
+##############################################################################
+# Linux build function
+##############################################################################
+#
+# This script file can be included in build scripts for Linux or run directly
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+build_obs() {
+    status "Build OBS"
+    trap "caught_error 'build app'" ERR
+    if [ -z "${CI}" ]; then
+        _backup_artifacts
+    fi
+
+    step "Configure OBS..."
+    _configure_obs
+
+    ensure_dir "${CHECKOUT_DIR}/"
+    step "Build OBS targets..."
+    cmake --build ${BUILD_DIR}
+}
+
+# Function to configure OBS build
+_configure_obs() {
+    ensure_dir "${CHECKOUT_DIR}"
+    status "Configuration of OBS build system..."
+    trap "caught_error 'configure build'" ERR
+    check_ccache
+
+    if [ "${TWITCH_CLIENTID}" -a "${TWICH_HASH}" ]; then
+        TWITCH_OPTIONS="-DTWITCH_CLIENTID=\"${TWITCH_CLIENTID}\" -DTWITCH_HASH=\"${TWITCH_HASH}\""
+    fi
+
+    if [ "${RESTREAM_CLIENTID}" -a "${RESTREAM_HASH}" ]; then
+        RESTREAM_OPTIONS="-DRESTREAM_CLIENTID=\"${RESTREAM_CLIENTID}\" -DRESTREAM_HASH=\"${RESTREAM_HASH}\""
+    fi
+
+    if [ "${YOUTUBE_CLIENTID}" -a "${YOUTUBE_CLIENTID_HASH}" -a "${YOUTUBE_SECRET}" -a "{YOUTUBE_SECRET_HASH}" ]; then
+        YOUTUBE_OPTIONS="-DYOUTUBE_CLIENTID=\"${YOUTUBE_CLIENTID}\" -DYOUTUBE_CLIENTID_HASH=\"${YOUTUBE_CLIENTID_HASH}\" -DYOUTUBE_SECRET=\"${YOUTUBE_SECRET}\" -DYOUTUBE_SECRET_HASH=\"${YOUTUBE_SECRET_HASH}\""
+    fi
+
+    if [ "${PORTABLE}" ]; then
+        PORTABLE_BUILD="ON"
+    fi
+
+    if [ "${DISABLE_PIPEWIRE}" ]; then
+        PIPEWIRE_OPTION="-DENABLE_PIPEWIRE=OFF"
+    fi
+
+    cmake -S . -B ${BUILD_DIR} -G Ninja \
+        -DCEF_ROOT_DIR="${DEPS_BUILD_DIR}/cef_binary_${LINUX_CEF_BUILD_VERSION:-${CI_LINUX_CEF_VERSION}}_linux64" \
+        -DCMAKE_BUILD_TYPE=${BUILD_CONFIG} \
+        -DLINUX_PORTABLE=${PORTABLE_BUILD:-OFF} \
+        -DENABLE_AJA=OFF \
+        ${PIPEWIRE_OPTION} \
+        ${YOUTUBE_OPTIONS} \
+        ${TWITCH_OPTIONS} \
+        ${RESTREAM_OPTIONS} \
+        ${CI:+-DENABLE_UNIT_TESTS=ON -DBUILD_FOR_DISTRIBUTION=${BUILD_FOR_DISTRIBUTION} -DOBS_BUILD_NUMBER=${GITHUB_RUN_ID}} \
+        ${QUIET:+-Wno-deprecated -Wno-dev --log-level=ERROR}
+}
+
+# Function to backup previous build artifacts
+_backup_artifacts() {
+    ensure_dir "${CHECKOUT_DIR}"
+    if [ -d ${BUILD_DIR} ]; then
+        status "Backup of old OBS build artifacts"
+
+        CUR_DATE=$(date +"%Y-%m-%d@%H%M%S")
+        NIGHTLY_DIR="${CHECKOUT_DIR}/nightly-${CUR_DATE}"
+        PACKAGE_NAME=$(find ${BUILD_DIR} -maxdepth 1 -name "*.deb" | sort -rn | head -1)
+
+        if [ "${PACKAGE_NAME}" ]; then
+            step "Back up $(basename "${PACKAGE_NAME}")..."
+            ensure_dir "${NIGHTLY_DIR}"
+            mv "../${BUILD_DIR}/$(basename "${PACKAGE_NAME}")" ${NIGHTLY_DIR}/
+            info "You can find ${PACKAGE_NAME} in ${NIGHTLY_DIR}"
+        fi
+    fi
+}
+
+build-obs-standalone() {
+    CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
+    PRODUCT_NAME="OBS-Studio"
+    DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+    source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+    source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh"
+
+    build_obs
+}
+
+print_usage() {
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "-p, --portable                 : Create portable build (default: off)\n" \
+            "--disable-pipewire             : Disable building with PipeWire support (default: off)\n" \
+            "--build-dir                    : Specify alternative build directory (default: build)\n"
+}
+
+build-obs-main() {
+    if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
+        while true; do
+            case "${1}" in
+                -h | --help ) print_usage; exit 0 ;;
+                -q | --quiet ) export QUIET=TRUE; shift ;;
+                -v | --verbose ) export VERBOSE=TRUE; shift ;;
+                -p | --portable ) export PORTABLE=TRUE; shift ;;
+                --disable-pipewire ) DISABLE_PIPEWIRE=TRUE; shift ;;
+                --build-dir ) BUILD_DIR="${2}"; shift 2 ;;
+                -- ) shift; break ;;
+                * ) break ;;
+            esac
+        done
+
+        build-obs-standalone
+    fi
+}
+
+build-obs-main $*

+ 82 - 0
CI/linux/03_package_obs.sh

@@ -0,0 +1,82 @@
+#!/bin/bash
+
+##############################################################################
+# Linux libobs plugin package function
+##############################################################################
+#
+# This script file can be included in build scripts for Linux or run directly
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+package_obs() {
+    status "Create Linux debian package"
+    trap "caught_error 'package app'" ERR
+
+    ensure_dir "${CHECKOUT_DIR}"
+
+    step "Package OBS..."
+    cmake --build ${BUILD_DIR} -t package
+
+    DEB_NAME=$(find ${BUILD_DIR} -maxdepth 1 -type f -name "obs*.deb" | sort -rn | head -1)
+
+    if [ "${DEB_NAME}" ]; then
+        mv ${DEB_NAME} ${BUILD_DIR}/${FILE_NAME}
+    else
+        error "ERROR No suitable OBS debian package generated"
+    fi
+}
+
+package-obs-standalone() {
+    PRODUCT_NAME="OBS-Studio"
+
+    CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
+    DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+    source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+    source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh"
+
+    step "Fetch OBS tags..."
+    git fetch origin --tags
+
+    GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
+    GIT_HASH=$(git rev-parse --short HEAD)
+    GIT_TAG=$(git describe --tags --abbrev=0)
+
+    if [ "${BUILD_FOR_DISTRIBUTION}" ]; then
+        VERSION_STRING="${GIT_TAG}"
+    else
+        VERSION_STRING="${GIT_TAG}-${GIT_HASH}"
+    fi
+
+    FILE_NAME="obs-studio-${VERSION_STRING}-Linux.deb"
+    package_obs
+}
+
+print_usage() {
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "--build-dir                    : Specify alternative build directory (default: build)\n"
+}
+
+package-obs-main() {
+    if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
+        while true; do
+            case "${1}" in
+                -h | --help ) print_usage; exit 0 ;;
+                -q | --quiet ) export QUIET=TRUE; shift ;;
+                -v | --verbose ) export VERBOSE=TRUE; shift ;;
+                --build-dir ) BUILD_DIR="${2}"; shift 2 ;;
+                -- ) shift; break ;;
+                * ) break ;;
+            esac
+        done
+
+        package-obs-standalone
+    fi
+}
+
+package-obs-main $*

+ 178 - 0
CI/macos/01_install_dependencies.sh

@@ -0,0 +1,178 @@
+#!/bin/bash
+
+##############################################################################
+# macOS dependency management function
+##############################################################################
+#
+# This script file can be included in build scripts for macOS or run directly.
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+install_obs-deps() {
+    status "Set up precompiled macOS OBS dependencies v${1}"
+    ensure_dir "${DEPS_BUILD_DIR}"
+    step "Download..."
+    ${CURLCMD:-curl} https://github.com/obsproject/obs-deps/releases/download/${1}/macos-deps-${1}.tar.gz
+    step "Unpack..."
+    /usr/bin/tar -xf "./macos-deps-${1}.tar.gz" -C /tmp
+}
+
+install_qt-deps() {
+    status "Set up precompiled dependency Qt v${1}"
+    ensure_dir "${DEPS_BUILD_DIR}"
+    step "Download..."
+    ${CURLCMD:-curl} https://github.com/obsproject/obs-deps/releases/download/${2}/macos-qt-${1}-${2}.tar.gz
+    step "Unpack..."
+    /usr/bin/tar -xf ./macos-qt-${1}-${2}.tar.gz -C /tmp
+    /usr/bin/xattr -r -d com.apple.quarantine /tmp/obsdeps
+}
+
+install_vlc() {
+    status "Set up dependency VLC v${1}"
+    ensure_dir "${DEPS_BUILD_DIR}"
+    unset _SKIP
+
+    if [ "${CI}" -a "${RESTORED_VLC}" ]; then
+        _SKIP=TRUE
+    elif [ -d "${DEPS_BUILD_DIR}/vlc-${1}" -a -f "${DEPS_BUILD_DIR}/vlc-${1}/include/vlc/vlc.h" ]; then
+        _SKIP=TRUE
+    fi
+
+    if [ -z "${_SKIP}" ]; then
+        step "Download..."
+        check_and_fetch "https://downloads.videolan.org/vlc/${1}/vlc-${1}.tar.xz" "${2}"
+        step "Unpack..."
+        /usr/bin/tar -xf vlc-${1}.tar.xz
+    else
+        step "Found existing VLC..."
+    fi
+}
+
+install_sparkle() {
+    status "Set up dependency Sparkle v${1}"
+    ensure_dir "${DEPS_BUILD_DIR}"
+    unset _SKIP
+
+    if [ "${CI}" -a "${RESTORED_SPARKLE}" ]; then
+        _SKIP=TRUE
+    elif [ -d "${DEPS_BUILD_DIR}/sparkle/Sparkle.framework" -a -f "${DEPS_BUILD_DIR}/sparkle/Sparkle.framework/Sparkle" ]; then
+        _SKIP=TRUE
+    fi
+
+    if [ -z "${_SKIP}" ]; then
+        step "Download..."
+        ${CURLCMD:-curl} https://github.com/sparkle-project/Sparkle/releases/download/${1}/Sparkle-${1}.tar.xz
+        step "Unpack..."
+        ensure_dir "${DEPS_BUILD_DIR}/sparkle"
+        /usr/bin/tar -xf ../Sparkle-${1}.tar.xz
+    else
+        step "Found existing Sparkle Framework..."
+    fi
+}
+
+install_cef() {
+    status "Set up dependency CEF v${1}"
+    ensure_dir "${DEPS_BUILD_DIR}"
+    unset _SKIP
+    
+    if [ "${CI}" -a "${RESTORED_CEF}" ]; then
+        _SKIP=TRUE
+    elif [ -d "${DEPS_BUILD_DIR}/cef_binary_${1}_macos_${ARCH:-x86_64}" -a -f "${DEPS_BUILD_DIR}/cef_binary_${1}_macos_${ARCH:-x86_64}/build/libcef_dll_wrapper/libcef_dll_wrapper.a" ]; then
+        _SKIP=TRUE
+    fi
+
+    if [ -z "${_SKIP}" ]; then
+        step "Download..."
+        ${CURLCMD:-curl} https://cdn-fastly.obsproject.com/downloads/cef_binary_${1}_macos_${ARCH:-x86_64}.tar.xz
+        step "Unpack..."
+        /usr/bin/tar -xf cef_binary_${1}_macos_${ARCH:-x86_64}.tar.xz
+        cd cef_binary_${1}_macos_${ARCH:-x86_64}
+        step "Fix tests..."
+
+        /usr/bin/sed -i '.orig' '/add_subdirectory(tests\/ceftests)/d' ./CMakeLists.txt
+        /usr/bin/sed -E -i '' 's/"10.(9|10)"/"'${MACOSX_DEPLOYMENT_TARGET:-${CI_MACOSX_DEPLOYMENT_TARGET}}'"/' ./cmake/cef_variables.cmake
+
+        step "Run CMake..."
+        check_ccache
+        cmake ${CCACHE_OPTIONS} ${QUIET:+-Wno-deprecated -Wno-dev --log-level=ERROR} \
+            -S . -B build \
+            -G Ninja \
+            -DPROJECT_ARCH=${CMAKE_ARCHS:-x86_64} \
+            -DCEF_COMPILER_FLAGS="-Wno-deprecated-copy" \
+            -DCMAKE_BUILD_TYPE=Release \
+            -DCMAKE_CXX_FLAGS="-std=c++11 -stdlib=libc++ -Wno-deprecated-declarations -Wno-unknown-warning-option" \
+            -DCMAKE_EXE_LINKER_FLAGS="-std=c++11 -stdlib=libc++" \
+            -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET:-${CI_MACOSX_DEPLOYMENT_TARGET}}
+
+        step "Build CEF v${1}..."
+        cmake --build build
+        mkdir -p build/libcef_dll
+    else
+        step "Found existing Chromium Embedded Framework and loader library..."
+    fi
+}
+
+install_dependencies() {
+    status "Install Homebrew dependencies"
+    trap "caught_error 'install_dependencies'" ERR
+
+    BUILD_DEPS=(
+        "obs-deps ${MACOS_DEPS_VERSION:-${CI_DEPS_VERSION}}"
+        "qt-deps ${QT_VERSION:-${CI_QT_VERSION}} ${MACOS_DEPS_VERSION:-${CI_DEPS_VERSION}}"
+        "cef ${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}"
+        "vlc ${VLC_VERSION:-${CI_VLC_VERSION}}"
+        "sparkle ${SPARKLE_VERSION:-${CI_SPARKLE_VERSION}}"
+    )
+
+    install_homebrew_deps
+
+    for DEPENDENCY in "${BUILD_DEPS[@]}"; do
+        set -- ${DEPENDENCY}
+        trap "caught_error ${DEPENDENCY}" ERR
+        FUNC_NAME="install_${1}"
+        ${FUNC_NAME} ${2} ${3}
+    done
+}
+
+install-dependencies-standalone() {
+    CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
+    PRODUCT_NAME="OBS-Studio"
+    DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+    source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+    source "${CHECKOUT_DIR}/CI/include/build_support_macos.sh"
+
+    status "Setup of OBS build dependencies"
+    check_macos_version
+    check_archs
+    install_dependencies
+}
+
+print_usage() {
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "-a, --architecture             : Specify build architecture (default: x86_64, alternative: arm64)\n"
+}
+
+install-dependencies-main() {
+    if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
+        while true; do
+            case "${1}" in
+                -h | --help ) print_usage; exit 0 ;;
+                -q | --quiet ) export QUIET=TRUE; shift ;;
+                -v | --verbose ) export VERBOSE=TRUE; shift ;;
+                -a | --architecture ) ARCH="${2}"; shift 2 ;;
+                -- ) shift; break ;;
+                * ) break ;;
+            esac
+        done
+
+        install-dependencies-standalone
+    fi
+}
+
+install-dependencies-main $*

+ 161 - 0
CI/macos/02_build_obs.sh

@@ -0,0 +1,161 @@
+#!/bin/bash
+
+##############################################################################
+# macOS build function
+##############################################################################
+#
+# This script file can be included in build scripts for macOS or run directly
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+build_obs() {
+    status "Build OBS"
+    trap "caught_error 'build app'" ERR
+
+    if [ -z "${CI}" ]; then
+        _backup_artifacts
+    fi
+    step "Configure OBS..."
+    _configure_obs
+
+    ensure_dir "${CHECKOUT_DIR}/"
+    step "Build OBS targets..."
+    cmake --build ${BUILD_DIR}
+}
+
+bundle_obs() {
+    status "Create relocatable macOS application bundle"
+    trap "caught_error 'package app'" ERR
+
+    ensure_dir "${CHECKOUT_DIR}"
+
+    step "Install OBS application bundle..."
+    cmake --install ${BUILD_DIR}
+}
+
+# Function to configure OBS build
+_configure_obs() {
+    if [ "${CODESIGN}" ]; then
+        read_codesign_ident
+    fi
+
+    ensure_dir "${CHECKOUT_DIR}"
+    status "Configure OBS build system..."
+    trap "caught_error 'configure build'" ERR
+    check_ccache
+
+    if [ "${TWITCH_CLIENTID}" -a "${TWICH_HASH}" ]; then
+        TWITCH_OPTIONS="-DTWITCH_CLIENTID=\"${TWITCH_CLIENTID}\" -DTWITCH_HASH=\"${TWITCH_HASH}\""
+    fi
+
+    if [ "${RESTREAM_CLIENTID}" -a "${RESTREAM_HASH}" ]; then
+        RESTREAM_OPTIONS="-DRESTREAM_CLIENTID=\"${RESTREAM_CLIENTID}\" -DRESTREAM_HASH=\"${RESTREAM_HASH}\""
+    fi
+
+    if [ "${YOUTUBE_CLIENTID}" -a "${YOUTUBE_CLIENTID_HASH}" -a "${YOUTUBE_SECRET}" -a "{YOUTUBE_SECRET_HASH}" ]; then
+        YOUTUBE_OPTIONS="-DYOUTUBE_CLIENTID=\"${YOUTUBE_CLIENTID}\" -DYOUTUBE_CLIENTID_HASH=\"${YOUTUBE_CLIENTID_HASH}\" -DYOUTUBE_SECRET=\"${YOUTUBE_SECRET}\" -DYOUTUBE_SECRET_HASH=\"${YOUTUBE_SECRET_HASH}\""
+    fi
+
+    if [ "${XCODE}" ]; then
+        GENERATOR="Xcode"
+    else
+        GENERATOR="Ninja"
+    fi
+
+    cmake -S . -B ${BUILD_DIR} -G ${GENERATOR} \
+        -DCEF_ROOT_DIR="${DEPS_BUILD_DIR}/cef_binary_${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}_macos_${ARCH:-x86_64}" \
+        -DVLC_PATH="${DEPS_BUILD_DIR}/vlc-${VLC_VERSION:-${CI_VLC_VERSION}}" \
+        -DCMAKE_PREFIX_PATH="/tmp/obsdeps;${DEPS_BUILD_DIR}/sparkle" \
+        -DBROWSER_LEGACY=$(test "${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}" -le 3770 && echo "ON" || echo "OFF") \
+        -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET:-${CI_MACOSX_DEPLOYMENT_TARGET}} \
+        -DCMAKE_OSX_ARCHITECTURES=${CMAKE_ARCHS} \
+        -DOBS_CODESIGN_LINKER=${CODESIGN_LINKER:-OFF} \
+        -DCMAKE_INSTALL_PREFIX=${BUILD_DIR}/install \
+        -DCMAKE_BUILD_TYPE=${BUILD_CONFIG} \
+        -DOBS_BUNDLE_CODESIGN_IDENTITY="${CODESIGN_IDENT:--}" \
+        ${YOUTUBE_OPTIONS} \
+        ${TWITCH_OPTIONS} \
+        ${RESTREAM_OPTIONS} \
+        ${CI:+-DENABLE_UNIT_TESTS=ON -DBUILD_FOR_DISTRIBUTION=${BUILD_FOR_DISTRIBUTION} -DOBS_BUILD_NUMBER=${GITHUB_RUN_ID}} \
+        ${QUIET:+-Wno-deprecated -Wno-dev --log-level=ERROR}
+}
+
+# Function to backup previous build artifacts
+_backup_artifacts() {
+    ensure_dir "${CHECKOUT_DIR}"
+    if [ -d "${BUILD_DIR}" ]; then
+        status "Backup old OBS build artifacts"
+
+        CUR_DATE=$(/bin/date +"%Y-%m-%d@%H%M%S")
+        NIGHTLY_DIR="${CHECKOUT_DIR}/nightly-${CUR_DATE}"
+        PACKAGE_NAME=$(/usr/bin/find "${BUILD_DIR}" -name "*.dmg" -depth 1 | sort -rn | head -1)
+
+        if [ -d "${BUILD_DIR}/install/OBS.app" ]; then
+            step "Back up OBS.app..."
+            ensure_dir "${NIGHTLY_DIR}"
+            /bin/mv "${CHECKOUT_DIR}/${BUILD_DIR}/install/OBS.app" "${NIGHTLY_DIR}/"
+            info "You can find OBS.app in ${NIGHTLY_DIR}"
+        fi
+
+        if [ "${PACKAGE_NAME}" ]; then
+            step "Back up $(basename "${PACKAGE_NAME}")..."
+            ensure_dir "${NIGHTLY_DIR}"
+            /bin/mv "../${BUILD_DIR}/$(basename "${PACKAGE_NAME}")" "${NIGHTLY_DIR}/"
+            info "You can find ${PACKAGE_NAME} in ${NIGHTLY_DIR}"
+        fi
+    fi
+}
+
+build-obs-standalone() {
+    CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
+    PRODUCT_NAME="OBS-Studio"
+    DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+    source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+    source "${CHECKOUT_DIR}/CI/include/build_support_macos.sh"
+
+    check_archs
+    check_macos_version
+    build_obs
+
+    if [ "${BUNDLE}" ]; then
+        bundle_obs
+    fi
+}
+
+print_usage() {
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "-a, --architecture             : Specify build architecture (default: x86_64, alternative: arm64)\n" \
+            "-c, --codesign                 : Codesign OBS and all libraries (default: ad-hoc only)\n" \
+            "-b, --bundle                   : Create relocatable OBS application bundle in build directory (default: build/install/OBS.app)\n" \
+            "--xcode                        : Create Xcode build environment instead of Ninja\n" \
+            "--build-dir                    : Specify alternative build directory (default: build)\n"
+}
+
+build-obs-main() {
+    if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
+        while true; do
+            case "${1}" in
+                -h | --help ) print_usage; exit 0 ;;
+                -q | --quiet ) export QUIET=TRUE; shift ;;
+                -v | --verbose ) export VERBOSE=TRUE; shift ;;
+                -a | --architecture ) ARCH="${2}"; shift 2 ;;
+                -c | --codesign ) CODESIGN=TRUE; shift ;;
+                -b | --bundle ) BUNDLE=TRUE; shift ;;
+                --xcode ) XCODE=TRUE; shift ;;
+                --build-dir ) BUILD_DIR="${2}"; shift 2 ;;
+                -- ) shift; break ;;
+                * ) break ;;
+            esac
+        done
+
+        build-obs-standalone
+    fi
+}
+
+build-obs-main $*

+ 185 - 0
CI/macos/03_package_obs.sh

@@ -0,0 +1,185 @@
+#!/bin/bash
+
+##############################################################################
+# macOS libobs plugin package function
+##############################################################################
+#
+# This script file can be included in build scripts for macOS or run directly
+#
+##############################################################################
+
+# Halt on errors
+set -eE
+
+package_obs() {
+    if [ "${CODESIGN}" ]; then
+        read_codesign_ident
+    fi
+
+    status "Create macOS disk image"
+    trap "caught_error 'package app'" ERR
+
+    info "/!\\ CPack will use an AppleScript to create the disk image, this will lead to a Finder window opening to adjust window settings. /!\\"
+
+    ensure_dir "${CHECKOUT_DIR}"
+
+    step "Package OBS..."
+    cmake --build ${BUILD_DIR} -t package
+
+    DMG_NAME=$(/usr/bin/find "${BUILD_DIR}" -type f -name "OBS-*.dmg" -depth 1 | sort -rn | head -1)
+
+    if [ "${DMG_NAME}" ]; then
+        mv "${DMG_NAME}" "${BUILD_DIR}/${FILE_NAME}"
+
+        step "Codesign OBS disk image..."
+        /usr/bin/codesign --force --sign "${CODESIGN_IDENT:--}" "${BUILD_DIR}/${FILE_NAME}"
+    else
+        error "ERROR No suitable OBS disk image generated"
+    fi
+}
+
+notarize_obs() {
+    status "Notarize OBS"
+    trap "caught_error 'notarizing app'" ERR
+
+    if ! exists brew; then
+        error "ERROR Homebrew not found - please install homebrew (https://brew.sh)"
+        exit 1
+    fi
+
+    if ! exists xcnotary; then
+        step "Install notarization dependency 'xcnotary'"
+        brew install akeru-inc/tap/xcnotary
+    fi
+
+    ensure_dir "${CHECKOUT_DIR}"
+
+    if [ "${NOTARIZE_IMAGE}" ]; then
+        trap "_caught_error_xcnotary '${NOTARIZE_IMAGE}'" ERR
+
+        step "Attach OBS disk image ${NOTARIZE_IMAGE}..."
+        hdiutil attach -readonly -noverify -noautoopen -quiet "${NOTARIZE_IMAGE}"
+
+        VOLUME_NAME=$(hdiutil info -plist | grep "/Volumes/OBS-" | sed 's/<string>\/Volumes\/\([^<]*\)<\/string>/\1/' | sed -e 's/^[[:space:]]*//')
+        PRECHECK="/Volumes/${VOLUME_NAME}/OBS.app"
+        NOTARIZE_TARGET="${NOTARIZE_IMAGE}"
+    elif [ "${NOTARIZE_BUNDLE}" ]; then
+        PRECHECK="${NOTARIZE_BUNDLE}"
+        NOTARIZE_TARGET="${NOTARIZE_BUNDLE}"
+    else
+        OBS_IMAGE="${BUILD_DIR}/${FILE_NAME}"
+
+        if [ -f "${OBS_IMAGE}" ]; then
+            OBS_BUNDLE=$(/usr/bin/find "${BUILD_DIR}/_CPack_Packages" -type d -name "OBS.app")
+            PRECHECK="${OBS_BUNDLE}"
+            NOTARIZE_TARGET="${OBS_IMAGE}"
+        else
+            error "No notarization application bundle ('OBS.app') or disk image ('${NOTARIZE_IMAGE:-${FILE_NAME}}') found"
+            return
+        fi
+    fi
+
+    step "Run notarization pre-checks on OBS.app..."
+    xcnotary precheck "${PRECHECK}"
+
+    if [ "$?" -eq 0 ]; then
+        read_codesign_ident
+        read_codesign_pass
+
+        step "Run xcnotary with ${NOTARIZE_TARGET}..."
+        xcnotary notarize "${NOTARIZE_TARGET}" --developer-account "${CODESIGN_IDENT_USER}" --developer-password-keychain-item "OBS-Codesign-Password" --provider "${CODESIGN_IDENT_SHORT}"
+    fi
+
+    if [ "${NOTARIZE_IMAGE}" -a -d "/Volumes/${VOLUME_NAME}" ]; then
+        step "Detach OBS disk image ${NOTARIZE_IMAGE}..."
+        hdiutil detach "/Volumes/${VOLUME_NAME}" -quiet
+    fi
+}
+
+_caught_error_xcnotary() {
+    error "ERROR during notarization of image '${1}'"
+
+    if [ -d "/Volumes/${1}" ]; then
+        step "Detach OBS disk image ${1}..."
+        hdiutil detach "/Volumes/${1}" -quiet
+    fi
+
+    cleanup
+    exit 1
+}
+
+package-obs-standalone() {
+    PRODUCT_NAME="OBS-Studio"
+
+    CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
+    DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
+    source "${CHECKOUT_DIR}/CI/include/build_support.sh"
+    source "${CHECKOUT_DIR}/CI/include/build_support_macos.sh"
+
+    check_macos_version
+    check_archs
+
+    step "Fetch OBS tags..."
+    /usr/bin/git fetch origin --tags
+
+    GIT_BRANCH=$(/usr/bin/git rev-parse --abbrev-ref HEAD)
+    GIT_HASH=$(/usr/bin/git rev-parse --short HEAD)
+    GIT_TAG=$(/usr/bin/git describe --tags --abbrev=0)
+
+    if [ "${BUILD_FOR_DISTRIBUTION}" ]; then
+        VERSION_STRING="${GIT_TAG}"
+    else
+        VERSION_STRING="${GIT_TAG}-${GIT_HASH}"
+    fi
+
+    if [ -z "${NOTARIZE_IMAGE}" -a -z "${NOTARIZE_BUNDLE}" ]; then
+        if [ "${ARCH}" = "arm64" ]; then
+            FILE_NAME="obs-studio-${VERSION_STRING}-macOS-Apple.dmg"
+        elif [ "${ARCH}" = "universal" ]; then
+            FILE_NAME="obs-studio-${VERSION_STRING}-macOS.dmg"
+        else
+            FILE_NAME="obs-studio-${VERSION_STRING}-macOS-Intel.dmg"
+        fi
+
+        package_obs
+    fi
+
+    if [ "${NOTARIZE}" ]; then
+        notarize_obs
+    fi
+}
+
+print_usage() {
+    echo -e "Usage: ${0}\n" \
+            "-h, --help                     : Print this help\n" \
+            "-q, --quiet                    : Suppress most build process output\n" \
+            "-v, --verbose                  : Enable more verbose build process output\n" \
+            "-c, --codesign                 : Codesign OBS and all libraries (default: ad-hoc only)\n" \
+            "-n, --notarize                 : Notarize OBS (default: off)\n" \
+            "--notarize-image [IMAGE]       : Specify existing OBS disk image for notarization\n" \
+            "--notarize-bundle [BUNDLE]     : Specify existing OBS application bundle for notarization\n" \
+            "--build-dir                    : Specify alternative build directory (default: build)\n"
+}
+
+package-obs-main() {
+    if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
+        while true; do
+            case "${1}" in
+                -h | --help ) print_usage; exit 0 ;;
+                -q | --quiet ) export QUIET=TRUE; shift ;;
+                -v | --verbose ) export VERBOSE=TRUE; shift ;;
+                -c | --codesign ) CODESIGN=TRUE; shift ;;
+                -n | --notarize ) NOTARIZE=TRUE; CODESIGN=TRUE; shift ;;
+                --build-dir ) BUILD_DIR="${2}"; shift 2 ;;
+                --notarize-image ) NOTARIZE_IMAGE="${2}"; NOTARIZE=TRUE; CODESIGN=TRUE; shift 2 ;;
+                --notarize-bundle ) NOTARIZE_BUNDLE="${2}"; NOTARIZE=TRUE; CODESIGN=TRUE; shift 2 ;;
+                -- ) shift; break ;;
+                * ) break ;;
+            esac
+        done
+
+        package-obs-standalone
+    fi
+}
+
+package-obs-main $*

+ 0 - 5
CI/scripts/macos/Brewfile

@@ -1,5 +0,0 @@
-tap "akeru-inc/tap"
-brew "cmake"
-brew "freetype"
-brew "cmocka"
-brew "akeru-inc/tap/xcnotary"

BIN
CI/scripts/macos/app/AppIcon.icns


+ 0 - 36
CI/scripts/macos/app/OBSPublicDSAKey.pem

@@ -1,36 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIIGPDCCBC4GByqGSM44BAEwggQhAoICAQCZZZ2y7H2GJmMfP4KQihJTJOoiGNUw
-mue6sqMbH+utRykRnSKBZux6R665eRFMpNgrgFO1TLLGbdD2U31KiGtCvFJOmOl3
-+QP055BuXjEG36NU7AWEFLAlbDlr/2D3oumq3Ib3iMnnr9RrVztJ2VFOvVio1eWr
-ZxboVwKPK8D6BqsWiv15vbYlJnTC4Fls6ySmdjVBxwoPlTaMu1ysi5DfbIZ93s5u
-aQt1FvXuWtPBWjyVUORcNbcWf49E5R2pV0OSBK95Hw2/wXz4vmj+w92dTePGnVaW
-Me4CoF5PIeZILwp6DCLStX4eW2WG1NChJTC8zeQ/3bMMoGyKM/MadyvrDqMywsKY
-caxkIwHrDKOEdXXGo80dIwZMMLipPA8DKhx5ojphfkeXjIhKSx+49knXT3ED5okE
-Wai7tGUXj/8D8sGh+7b+AVsdujvr4v8WQaZiKUOZ2IIHOg3VLz9T9v0zet1Yt987
-KNymFcp2CHeJ6KnDP/ZGQ6Nl0HsPxUgscsXV+R2FEc8Q1j0Ukkuxnopa0E4/huUu
-gjyRzpXD734qFMDf7LcXca6qNjBor6gVj5sRyRKCpZ+KQfMUlr8jp506ztYSyeJu
-dxJV30tQgztwkbrs02CqOt4Z3Peo6sdht7hWKSPVwmja3tq8/TfUSSoo6wKYN9/w
-Mf3dVeRF8hCzJQIVAJnzuzmzQhCKPiQnl3jh5qGII2XfAoICAQCCVATAff89ceHj
-ROHEbHTQFpVxJ/kRZPfxnU46DSw79Tih7tthV68oakPSOTP3cx/Tga0GwogarZ9N
-F2VVan5w9OQSSewXsr5UDT5bnmJF+h+JB7TMy+sXZBYobUqjlUd5VtKc8RsN86P4
-s7xbK0mA+hfe+27r18JT81/eH3xUfh7UOUGSdMN2Ch9f7RFSMZIgUAZUzu2K3ODp
-hPgtc2QJ8QVAp7GLvQgw8ZUME/ChZslyBIyJvYgUIxfxlgRWYro5pQT7/ngkgdXo
-wlghHKkldwMuY3zaFdhPnFNuEUEtc18ILsbz0+AnagCUd6n+3safskCRqLIHMOY6
-iLBSZPX9hJQhVCqSqz1VNDDww8FNa/fojJ1Lr/TI0I+0Ib2pCiY2LChXUqGY5SLZ
-2KNs5qFsyZP+I0L8YsGwqvUYyFwk7Ok224n0NtaOwqpLCrtXd/i6DaDNiaoJuwJC
-1ELCfaZivorgkC5rhBt2H7qWUAR+EtrFE/gb0k/G5EIhjYql7onGbX+G2re38vQA
-fg1pzguhig2dafP/BxMLZrn1Gg61xzmEYPuS9gclktaf675srv8GVb46VkOxXL+D
-YvTmpJPP7UUOVlmAMCo4j4y09MW3jq9TDp42VTLeZVubyjslGnavlnq1O+ZyXUye
-1FMeby65sIbSHHHwoFnRv3hLSEXI5gOCAgYAAoICAQCUkYnZkPfHfOJZI403xUYP
-CE/bLpkza074Xo6EXElsWRnpQgNTx+JFOvItgj3v0OkIqDin9UredKOwfkiftslV
-jxUVKA6I5kwnGvCpvTpQMLyLjq+VQr+J2D6eId6tV/iajhdu5r4JThU8KllT7Ywb
-NAur34ftLNCVAMRUaDNeEoHfePgderW384e+lbvpmtifmBluammGSxxRtUsdjvJZ
-BFkhaJu86CKxcU7D1lbPVOtV/jaxz6d16VdGcfBdi2LzXZzZtYpT9XGPX3NF+xii
-spAURWsoe11LTRXF+eJhgCm5iIDN3kh1HEQKYKAVpmrcM0aFzk/NpS+tFyU72vaq
-IRSSJw/aa1oELOAakG5oPldc4RcYWl32sbnVwXHO7TZvgTrBSC10o65MAC5CHP/s
-b07heDYAIt7re7szvOYq+c/9zAMAlu3pcO8MqaXYMmybdHBXHQ2b+DdJWHmIUWcX
-CbUzr09vzGkJAvqsXqbmJPr8aixrO75DhT0iDTILLWe/GWK51nf+Tg0pNxVgGyAl
-BqvRqqo7SSDu9FMkwQesFFHhuoHLyEHwVPJ+sMQTNwQcm9c6YuW8EYDRSkeKLWYk
-3fkjG+Pe9uVE8a1taDg3FjSY0UqjUT6XMw+i0Lajyus2L6wFBwrrGM6E4xa6x1CC
-MGjmuSOlPA1umQsToIcO4g==
------END PUBLIC KEY-----

BIN
CI/scripts/macos/app/big_sur_icon_template.psd


BIN
CI/scripts/macos/app/dylibbundler


+ 0 - 17
CI/scripts/macos/app/entitlements.plist

@@ -1,17 +0,0 @@
-<!--?xml version="1.0" encoding="UTF-8"?-->
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-    <dict>
-        <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
-        <true/>
-        <key>com.apple.security.device.camera</key>
-        <true/>
-        <key>com.apple.security.device.audio-input</key>
-        <true/>
-        <key>com.apple.security.cs.disable-library-validation</key>
-        <true/>
-        <!-- Allows @executable_path to load libaries from within the .app bundle. -->
-        <key>com.apple.security.cs.allow-dyld-environment-variables</key>
-        <true/>
-    </dict>
-</plist>

BIN
CI/scripts/macos/app/obs.icns


+ 0 - 12
CI/scripts/macos/helpers/helper-entitlements.plist

@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-   <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
-   <true/>
-   <key>com.apple.security.cs.disable-library-validation</key>
-   <true/>
-   <key>com.apple.security.cs.allow-jit</key>
-   <true/>
-</dict>
-</plist>

+ 0 - 8
CI/scripts/macos/helpers/helper-gpu-entitlements.plist

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-    <key>com.apple.security.cs.allow-jit</key>
-    <true/>
-</dict>
-</plist>

+ 0 - 10
CI/scripts/macos/helpers/helper-plugin-entitlements.plist

@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
-    <true/>
-    <key>com.apple.security.cs.disable-library-validation</key>
-    <true/>
-</dict>
-</plist>

+ 0 - 8
CI/scripts/macos/helpers/helper-renderer-entitlements.plist

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-    <key>com.apple.security.cs.allow-jit</key>
-    <true/>
-</dict>
-</plist>

BIN
CI/scripts/macos/package/background.tiff


+ 0 - 28
CI/scripts/macos/package/settings.json.template

@@ -1,28 +0,0 @@
-{
-    "title": "OBS-Studio $$VERSION$$",
-    "background": "$$CI_PATH$$/package/background.tiff",
-    "icon": "$$CI_PATH$$/app/AppIcon.icns",
-    "format": "ULFO",
-    "icon-size": 96,
-    "window": {
-        "position": {
-            "x": 100,
-            "y": 100
-        },
-        "size": {
-            "width": 540,
-            "height": 380
-        }
-    },
-    "contents": [{
-        "x": 124,
-        "y": 180,
-        "type": "file",
-        "path": "$$BUNDLE_PATH$$/OBS.app"
-    }, {
-        "x": 416,
-        "y": 180,
-        "type": "link",
-        "path": "/Applications"
-    }]
-}

BIN
CI/scripts/macos/package/src/background.png


BIN
CI/scripts/macos/package/src/[email protected]


+ 0 - 1
CI/scripts/macos/package/src/makeRetinaBG

@@ -1 +0,0 @@
-tiffutil -cathidpicheck background.png [email protected] -out background.tiff

+ 177 - 0
CI/windows/01_install_dependencies.ps1

@@ -0,0 +1,177 @@
+Param(
+    [Switch]$Help = $(if (Test-Path variable:Help) { $Help }),
+    [Switch]$Quiet = $(if (Test-Path variable:Quiet) { $Quiet }),
+    [Switch]$Verbose = $(if (Test-Path variable:Verbose) { $Verbose }),
+    [ValidateSet("32-bit", "64-bit")]
+    [String]$BuildArch = $(if (Test-Path variable:BuildArch) { "${BuildArch}" } else { (Get-CimInstance CIM_OperatingSystem).OSArchitecture })
+)
+
+##############################################################################
+# Windows dependency management function
+##############################################################################
+#
+# This script file can be included in build scripts for Windows or run
+# directly
+#
+##############################################################################
+
+$ErrorActionPreference = "Stop"
+
+Function Install-obs-deps {
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String]$Version
+    )
+
+    Write-Status "Setup for pre-built Windows OBS dependencies v${Version}"
+    Ensure-Directory $DepsBuildDir
+
+    if (!(Test-Path "$DepsBuildDir/dependencies${Version}")) {
+
+        Write-Step "Download..."
+        $ProgressPreference = $(if ($Quiet.isPresent) { "SilentlyContinue" } else { "Continue" })
+        Invoke-WebRequest -Uri "https://cdn-fastly.obsproject.com/downloads/dependencies${Version}.zip" -UseBasicParsing -OutFile "dependencies${Version}.zip"
+        $ProgressPreference = "Continue"
+
+        Write-Step "Unpack..."
+
+        Expand-Archive -Path "dependencies${Version}.zip"
+    } else {
+        Write-Step "Found existing pre-built dependencies..."
+    }
+}
+
+function Install-qt-deps {
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String]$Version
+    )
+
+    Write-Status "Setup for pre-built dependency Qt v${Version}"
+    Ensure-Directory $DepsBuildDir
+
+    if (!(Test-Path "$DepsBuildDir/Qt_${Version}")) {
+
+        Write-Step "Download..."
+        $ProgressPreference = $(if ($Quiet.isPresent) { 'SilentlyContinue' } else { 'Continue' })
+        Invoke-WebRequest -Uri "https://cdn-fastly.obsproject.com/downloads/Qt_${Version}.7z" -UseBasicParsing -OutFile "Qt_${Version}.7z"
+        $ProgressPreference = "Continue"
+        
+        Write-Step "Unpack..."
+
+        # TODO: Replace with zip and properly package Qt to share directory with other deps
+        Invoke-Expression "7z x Qt_${Version}.7z"
+        Move-Item -Path "${Version}" -Destination "Qt_${Version}"
+    } else {
+        Write-Step "Found existing pre-built Qt..."
+    }
+}
+
+function Install-vlc {
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String]$Version
+    )
+
+    Write-Status "Setup for dependency VLC v${Version}"
+    Ensure-Directory $DepsBuildDir
+
+    if (!((Test-Path "$DepsBuildDir/vlc-${Version}") -and (Test-Path "$DepsBuildDir/vlc-${Version}/include/vlc/vlc.h"))) {
+        Write-Step "Download..."
+        $ProgressPreference = $(if ($Quiet.isPresent) { 'SilentlyContinue' } else { 'Continue' })
+        Invoke-WebRequest -Uri "https://cdn-fastly.obsproject.com/downloads/vlc.zip" -UseBasicParsing -OutFile "vlc_${Version}.zip"
+        $ProgressPreference = "Continue"
+
+        Write-Step "Unpack..."
+        # Expand-Archive -Path "vlc_${Version}.zip"
+        Invoke-Expression "7z x vlc_${Version}.zip -ovlc"
+        Move-Item -Path vlc -Destination "vlc-${Version}"
+    } else {
+        Write-Step "Found existing VLC..."
+    }
+}
+
+function Install-cef {
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String]$Version
+    )
+    Write-Status "Setup for dependency CEF v${Version} - ${BuildArch}"
+
+    Ensure-Directory $DepsBuildDir
+    $ArchSuffix = "$(if ($BuildArch -eq "64-bit") { "x64" } else { "x86" })"
+
+    if (!((Test-Path "${DepsBuildDir}/cef_binary_${Version}_windows_${ArchSuffix}") -and (Test-Path "${DepsBuildDir}/cef_binary_${Version}_windows_${ArchSuffix}/build/libcef_dll_wrapper/Release/libcef_dll_wrapper.lib"))) {
+        Write-Step "Download..."
+        $ProgressPreference = $(if ($Quiet.isPresent) { 'SilentlyContinue' } else { 'Continue' })
+        Invoke-WebRequest -Uri "https://cdn-fastly.obsproject.com/downloads/cef_binary_${Version}_windows_${ArchSuffix}.zip" -UseBasicParsing -OutFile "cef_binary_${Version}_windows_${ArchSuffix}.zip"
+        $ProgressPreference = "Continue"
+
+        Write-Step "Unpack..."
+        Invoke-Expression "7z x cef_binary_${Version}_windows_${ArchSuffix}.zip"
+    } else {
+        Write-Step "Found existing CEF framework and loader library..."
+    }
+}
+
+function Install-Dependencies {
+    Param(
+        [String]$BuildArch = $(if (Test-Path variable:BuildArch) { "${BuildArch}" })
+    )
+
+    if($Choco.isPresent) {
+        Install-Windows-Dependencies
+    }
+
+    $BuildDependencies = @(
+        @('obs-deps', $WindowsDepsVersion),
+        @('qt-deps', $WindowsQtVersion),
+        @('vlc', $WindowsVlcVersion),
+        @('cef', $WindowsCefVersion)
+    )
+
+    Foreach($Dependency in ${BuildDependencies}) {
+        $DependencyName = $Dependency[0]
+        $DependencyVersion = $Dependency[1]
+
+        $FunctionName = "Install-${DependencyName}"
+        Invoke-Expression "${FunctionName} -Version ${DependencyVersion}"
+    }
+
+    Ensure-Directory ${CheckoutDir}
+}
+
+function Install-Dependencies-Standalone {
+    $ProductName = "OBS-Studio"
+    $CheckoutDir = Resolve-Path -Path "$PSScriptRoot\..\.."
+    $DepsBuildDir = "${CheckoutDir}/../obs-build-dependencies"
+    $ObsBuildDir = "${CheckoutDir}/../obs-studio"
+
+    . ${CheckoutDir}/CI/include/build_support_windows.ps1
+
+    Write-Status "Setup of OBS build dependencies"
+    Install-Dependencies
+}
+
+function Print-Usage {
+    $Lines = @(
+        "Usage: ${_ScriptName}",
+        "-Help                    : Print this help",
+        "-Quiet                   : Suppress most build process output",
+        "-Verbose                 : Enable more verbose build process output",
+        "-Choco                   : Enable automatic dependency installation via Chocolatey - Default: off"
+        "-BuildArch               : Build architecture to use (32-bit or 64-bit) - Default: local architecture"
+    )
+
+    $Lines | Write-Host
+}
+
+if(!(Test-Path variable:_RunObsBuildScript)) {
+    $_ScriptName = "$($MyInvocation.MyCommand.Name)"
+    if($Help.isPresent) {
+        Print-Usage
+        exit 0
+    }
+
+    Install-Dependencies-Standalone
+}

+ 116 - 0
CI/windows/02_build_obs.ps1

@@ -0,0 +1,116 @@
+Param(
+    [Switch]$Help = $(if (Test-Path variable:Help) { $Help }),
+    [Switch]$Quiet = $(if (Test-Path variable:Quiet) { $Quiet }),
+    [Switch]$Verbose = $(if (Test-Path variable:Verbose) { $Verbose }),
+    [String]$BuildDirectory = $(if (Test-Path variable:BuildDirectory) { "${BuildDirectory}" } else { "build" }),
+    [ValidateSet("32-bit", "64-bit")]
+    [String]$BuildArch = $(if (Test-Path variable:BuildArch) { "${BuildArch}" } else { (Get-CimInstance CIM_OperatingSystem).OSArchitecture }),
+    [ValidateSet("Release", "RelWithDebInfo", "MinSizeRel", "Debug")]
+    [String]$BuildConfiguration = $(if (Test-Path variable:BuildConfiguration) { "${BuildConfiguration}" } else { "RelWithDebInfo" })
+)
+
+##############################################################################
+# Windows libobs library build function
+##############################################################################
+#
+# This script file can be included in build scripts for Windows or run
+# directly
+#
+##############################################################################
+
+$ErrorActionPreference = "Stop"
+
+function Build-OBS {
+    Param(
+        [String]$BuildDirectory = $(if (Test-Path variable:BuildDirectory) { "${BuildDirectory}" }),
+        [String]$BuildArch = $(if (Test-Path variable:BuildArch) { "${BuildArch}" }),
+        [String]$BuildConfiguration = $(if (Test-Path variable:BuildConfiguration) { "${BuildConfiguration}" })
+    )
+
+    Write-Status "Build OBS"
+
+    Configure-OBS
+
+    Ensure-Directory ${CheckoutDir}
+    Write-Step "Build OBS targets..."
+
+    Invoke-Expression "cmake --build $(if($BuildArch -eq "64-bit") { "build64" } else { "build32" }) --config ${BuildConfiguration}"
+}
+
+function Configure-OBS {
+    Ensure-Directory ${CheckoutDir}
+    Write-Status "Configuration of OBS build system..."
+
+    # TODO: Clean up archive and directory naming across dependencies
+    $QtDirectory = "${CheckoutDir}/../obs-build-dependencies/Qt_${WindowsQtVersion}/msvc2019$(if (${BuildArch} -eq "64-bit") { "_64" })"
+    $DepsDirectory = "${CheckoutDir}/../obs-build-dependencies/dependencies${WindowsDepsVersion}/win$(if (${BuildArch} -eq "64-bit") { "64" } else { "32" })"
+    $CefDirectory = "${CheckoutDir}/../obs-build-dependencies/cef_binary_${WindowsCefVersion}_windows_$(if (${BuildArch} -eq "64-bit") { "x64" } else { "x86" })"
+    $CmakePrefixPath = "${QtDirectory};${DepsDirectory}/bin;${DepsDirectory}"
+    $BuildDirectoryActual = "${BuildDirectory}$(if (${BuildArch} -eq "64-bit") { "64" } else { "32" })"
+    $GeneratorPlatform = "$(if (${BuildArch} -eq "64-bit") { "x64" } else { "Win32" })"
+
+    $CmakeCommand = @(
+        "-S . -B `"${BuildDirectoryActual}`"",
+        "-G `"${CmakeGenerator}`"",
+        "-DCMAKE_GENERATOR_PLATFORM=`"${GeneratorPlatform}`"",
+        "-DCMAKE_SYSTEM_VERSION=`"${CmakeSystemVersion}`"",
+        "-DCMAKE_PREFIX_PATH=`"${CmakePrefixPath}`"",
+        "-DCEF_ROOT_DIR=`"${CefDirectory}`"",
+        "-DVLC_PATH=`"${CheckoutDir}/../obs-build-dependencies/vlc-${WindowsVlcVersion}`"",
+        "-DCMAKE_INSTALL_PREFIX=`"${BuildDirectoryActual}/install`"",
+        "-DVIRTUALCAM_GUID=`"${Env:VIRTUALCAM-GUID}`"",
+        "-DTWITCH_CLIENTID=`"${Env:TWITCH_CLIENTID}`"",
+        "-DTWITCH_HASH=`"${Env:TWITCH_HASH}`"",
+        "-DRESTREAM_CLIENTID=`"${Env:RESTREAM_CLIENTID}`"",
+        "-DRESTREAM_HASH=`"${Env:RESTREAM_HASH}`"",
+        "-DYOUTUBE_CLIENTID=`"${Env:YOUTUBE_CLIENTID}`"",
+        "-DYOUTUBE_CLIENTID_HASH=`"${Env:YOUTUBE_CLIENTID_HASH}`"",
+        "-DYOUTUBE_SECRET=`"${Env:YOUTUBE_SECRET}`"",
+        "-DYOUTUBE_SECRET_HASH=`"${Env:YOUTUBE_SECRET_HASH}`"",
+        "-DCOPIED_DEPENDENCIES=OFF",
+        "-DCOPY_DEPENDENCIES=ON",
+        "-DBUILD_FOR_DISTRIBUTION=`"$(if (Test-Path Env:BUILD_FOR_DISTRIBUTION) { "ON" } else { "OFF" })`"",
+        "$(if (Test-Path Env:CI) { "-DOBS_BUILD_NUMBER=${Env:GITHUB_RUN_ID}" })",
+        "$(if (Test-Path Variable:$Quiet) { "-Wno-deprecated -Wno-dev --log-level=ERROR" })"
+    )
+
+    Invoke-Expression "cmake ${CmakeCommand}"
+
+    Ensure-Directory ${CheckoutDir}
+}
+
+function Build-OBS-Standalone {
+    $ProductName = "OBS-Studio"
+
+    $CheckoutDir = Resolve-Path -Path "$PSScriptRoot\..\.."
+    $DepsBuildDir = "${CheckoutDir}/../obs-build-dependencies"
+    $ObsBuildDir = "${CheckoutDir}/../obs-studio"
+
+    . ${CheckoutDir}/CI/include/build_support_windows.ps1
+
+    Build-OBS
+}
+
+function Print-Usage {
+    $Lines = @(
+        "Usage: ${_ScriptName}",
+        "-Help                    : Print this help",
+        "-Quiet                   : Suppress most build process output",
+        "-Verbose                 : Enable more verbose build process output",
+        "-BuildDirectory          : Directory to use for builds - Default: build64 on 64-bit systems, build32 on 32-bit systems",
+        "-BuildArch               : Build architecture to use (32-bit or 64-bit) - Default: local architecture",
+        "-BuildConfiguration      : Build configuration to use - Default: RelWithDebInfo"
+    )
+
+    $Lines | Write-Host
+}
+
+if(!(Test-Path variable:_RunObsBuildScript)) {
+    $_ScriptName = "$($MyInvocation.MyCommand.Name)"
+    if($Help.isPresent) {
+        Print-Usage
+        exit 0
+    }
+
+    Build-OBS-Standalone
+}

+ 155 - 0
CI/windows/03_package_obs.ps1

@@ -0,0 +1,155 @@
+Param(
+    [Switch]$Help = $(if (Test-Path variable:Help) { $Help }),
+    [Switch]$Quiet = $(if (Test-Path variable:Quiet) { $Quiet }),
+    [Switch]$Verbose = $(if (Test-Path variable:Verbose) { $Verbose }),
+    [Switch]$BuildInstaller = $(if ($BuildInstaller.isPresent) { $BuildInstaller }),
+    [Switch]$CombinedArchs = $(if ($CombinedArchs.isPresent) { $CombinedArchs }),
+    [String]$BuildDirectory = $(if (Test-Path variable:BuildDirectory) { "${BuildDirectory}" } else { "build" }),
+    [ValidateSet("32-bit", "64-bit")]
+    [String]$BuildArch = $(if (Test-Path variable:BuildArch) { "${BuildArch}" } else { (Get-CimInstance CIM_OperatingSystem).OSArchitecture }),
+    [ValidateSet("Release", "RelWithDebInfo", "MinSizeRel", "Debug")]
+    [String]$BuildConfiguration = $(if (Test-Path variable:BuildConfiguration) { "${BuildConfiguration}" } else { "RelWithDebInfo" })
+)
+
+##############################################################################
+# Windows OBS package function
+##############################################################################
+#
+# This script file can be included in build scripts for Windows or run
+# directly
+#
+##############################################################################
+
+$ErrorActionPreference = "Stop"
+
+function Package-OBS {
+    Param(
+        [String]$BuildDirectory = $(if (Test-Path variable:BuildDirectory) { "${BuildDirectory}" }),
+        [String]$BuildArch = $(if (Test-Path variable:BuildArch) { "${BuildArch}" }),
+        [String]$BuildConfiguration = $(if (Test-Path variable:BuildConfiguration) { "${BuildConfiguration}" })
+    )
+
+    Write-Status "Package plugin ${ProductName}"
+    Ensure-Directory ${CheckoutDir}
+
+    if ($CombinedArchs.isPresent) {
+        if (!(Test-Path env:obsInstallerTempDir)) {
+            $Env:obsInstallerTempDir = "${CheckoutDir}/install_temp"
+        }
+
+        if (!(Test-Path ${CheckoutDir}/install_temp/bin/64bit)) {
+            Write-Step "Build 64-bit OBS..."
+            Invoke-Expression "cmake -S . -B `"${BuildDirectory}64`" -DCOPIED_DEPENDENCIES=OFF -DCOPY_DEPENDENCIES=ON"
+            Invoke-Expression "cmake --build `"${BuildDirectory}64`" --config `"${BuildConfiguration}`""
+        }
+
+        if (!(Test-Path ${CheckoutDir}/install_temp/bin/32bit)) {
+            Write-Step "Build 32-bit OBS..."
+            Invoke-Expression "cmake -S . -B `"${BuildDirectory}32`" -DCOPIED_DEPENDENCIES=OFF -DCOPY_DEPENDENCIES=ON"
+            Invoke-Expression "cmake --build `"${BuildDirectory}32`" --config `"${BuildConfiguration}`""
+        }
+
+        Write-Step "Prepare Installer run..."
+        Invoke-Expression "cmake -S . -B build -DINSTALLER_RUN=ON -DCMAKE_INSTALL_PREFIX=`"${CheckoutDir}/build/install`""
+        Write-Step "Execute Installer run..."
+        Invoke-Expression "cmake --build build --config `"${BuildConfiguration}`" -t install"
+
+        $CompressVars = @{
+            Path = "${CheckoutDir}/build/install/*"
+            CompressionLevel = "Optimal"
+            DestinationPath = "${FileName}-Windows.zip"
+        }
+
+        Write-Step "Creating zip archive..."
+
+        $ProgressPreference = $(if ($Quiet.isPresent) { 'SilentlyContinue' } else { 'Continue' })
+        Compress-Archive -Force @CompressVars
+        $ProgressPreference = 'Continue'
+
+    } elseif ($BuildArch -eq "64-bit") {
+        Write-Step "Install 64-bit OBS..."
+        Invoke-Expression "cmake --build `"${BuildDirectory}64`" --config ${BuildConfiguration} -t install"
+
+        $CompressVars = @{
+            Path = "${CheckoutDir}/build64/install/bin", "${CheckoutDir}/build64/install/data", "${CheckoutDir}/build64/install/obs-plugins"
+            CompressionLevel = "Optimal"
+            DestinationPath = "${FileName}-Win64.zip"
+        }
+
+        Write-Step "Creating zip archive..."
+
+        $ProgressPreference = $(if ($Quiet.isPresent) { 'SilentlyContinue' } else { 'Continue' })
+        Compress-Archive -Force @CompressVars
+        $ProgressPreference = 'Continue'
+
+    } elseif ($BuildArch -eq "32-bit") {
+        Write-Step "Install 32-bit OBS..."
+        Invoke-Expression "cmake --build `"${BuildDirectory}32`" --config ${BuildConfiguration} -t install"
+
+        $CompressVars = @{
+            Path = "${CheckoutDir}/build32/install/bin", "${CheckoutDir}/build32/install/data", "${CheckoutDir}/build32/install/obs-plugins"
+            CompressionLevel = "Optimal"
+            DestinationPath = "${FileName}-Win32.zip"
+        }
+
+        Write-Step "Creating zip archive..."
+
+        $ProgressPreference = $(if ($Quiet.isPresent) { 'SilentlyContinue' } else { 'Continue' })
+        Compress-Archive -Force @CompressVars
+        $ProgressPreference = 'Continue'
+
+    }
+}
+
+function Package-OBS-Standalone {
+    $ProductName = "OBS-Studio"
+    $CheckoutDir = Resolve-Path -Path "$PSScriptRoot\..\.."
+
+    . ${CheckoutDir}/CI/include/build_support_windows.ps1
+
+    Write-Step "Fetch OBS tags..."
+    $null = git fetch origin --tags
+
+    Ensure-Directory ${CheckoutDir}
+    $GitBranch = git rev-parse --abbrev-ref HEAD
+    $GitHash = git rev-parse --short HEAD
+    $ErrorActionPreference = "SilentlyContinue"
+    $GitTag = git describe --tags --abbrev=0
+    $ErrorActionPreference = "Stop"
+
+    if(Test-Path variable:BUILD_FOR_DISTRIBUTION) {
+        $VersionString = "${GitTag}"
+    } else {
+        $VersionString = "${GitTag}-${GitHash}"
+    }
+
+    $FileName = "${ProductName}-${VersionString}"
+
+    Package-OBS
+}
+
+function Print-Usage {
+    $Lines = @(
+        "Usage: ${_ScriptName}",
+        "-Help                    : Print this help",
+        "-Quiet                   : Suppress most build process output",
+        "-Verbose                 : Enable more verbose build process output",
+        "-CombinedArchs           : Create combined architecture package",
+        "-BuildDirectory          : Directory to use for builds - Default: build64 on 64-bit systems, build32 on 32-bit systems",
+        "-BuildArch               : Build architecture to use (32-bit or 64-bit) - Default: local architecture",
+        "-BuildConfiguration      : Build configuration to use - Default: RelWithDebInfo"
+    )
+
+    $Lines | Write-Host
+}
+
+
+if(!(Test-Path variable:_RunObsBuildScript)) {
+    $_ScriptName = "$($MyInvocation.MyCommand.Name)"
+    if($Help.isPresent) {
+        Print-Usage
+        exit 0
+    }
+
+    Package-OBS-Standalone
+}

+ 72 - 289
CMakeLists.txt

@@ -1,309 +1,92 @@
-cmake_minimum_required(VERSION 3.10)
-
-if (UNIX AND POLICY CMP0072)
-	# In case of both legacy and glvnd OpenGL libraries found. Prefer GLVND
-	cmake_policy(SET CMP0072 NEW)
+cmake_minimum_required(VERSION 3.16...3.21)
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules")
+include(VersionConfig)
+
+# Prohibit in-source builds
+if("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
+  message(
+    FATAL_ERROR
+      "OBS: You cannot build in a source directory (or any directory with "
+      "CMakeLists.txt file). Please make a build subdirectory. Feel free to "
+      "remove CMakeCache.txt and CMakeFiles.")
 endif()
 
-project(obs-studio)
-
-option(DEBUG_FFMPEG_MUX "Debug FFmpeg muxer subprocess" FALSE)
-
+project(obs-studio VERSION ${OBS_VERSION_CANONICAL})
 set_property(GLOBAL PROPERTY USE_FOLDERS ON)
 
-if(WIN32)
-	cmake_minimum_required(VERSION 3.16)
-
-	# Check for Win SDK version 10.0.20348 or above
-	if(MSVC)
-		message(STATUS "Windows API version is ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
-		string(REPLACE "." ";" WINAPI_VER "${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
-
-		list(GET WINAPI_VER 0 WINAPI_VER_MAJOR)
-		list(GET WINAPI_VER 1 WINAPI_VER_MINOR)
-		list(GET WINAPI_VER 2 WINAPI_VER_BUILD)
-
-		set(WINAPI_COMPATIBLE FALSE)
-		if(WINAPI_VER_MAJOR EQUAL 10)
-			if (WINAPI_VER_MINOR EQUAL 0)
-				if (WINAPI_VER_BUILD GREATER_EQUAL 20348)
-					set(WINAPI_COMPATIBLE TRUE)
-				endif()
-			else()
-				set(WINAPI_COMPATIBLE TRUE)
-			endif()
-		elseif(WINAPI_VER_MAJOR GREATER 10)
-			set(WINAPI_COMPATIBLE TRUE)
-		endif()
-
-		if(NOT WINAPI_COMPATIBLE)
-			message(FATAL_ERROR "OBS requires Windows 10 SDK version 10.0.20348.0 and above to compile.\nPlease download the most recent Windows 10 SDK in order to compile.")
-		endif()
-	endif()
-
-	if (QTDIR OR DEFINED ENV{QTDIR} OR DEFINED ENV{QTDIR32} OR DEFINED ENV{QTDIR64})
-		# Qt path set by user or env var
-	else()
-		set(QTDIR "" CACHE PATH "Path to Qt (e.g. C:/Qt/5.7/msvc2015_64)")
-		message(WARNING "QTDIR variable is missing.  Please set this variable to specify path to Qt (e.g. C:/Qt/5.7/msvc2015_64)")
-	endif()
-	if (DepsPath OR DEFINED ENV{DepsPath} OR DEFINED ENV{DepsPath32} OR DEFINED ENV{DepsPath64})
-		# Dependencies path set by user or env var
-	else()
-		set(DepsPath "" CACHE PATH "Path to compiled dependencies (e.g. D:/dependencies/win64)")
-		message(WARNING "DepsPath variable is missing.  Please set this variable to specify path to compiled dependencies (e.g. D:/dependencies/win64)")
-	endif()
-endif()
-
-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
-set(ENABLE_SCRIPTING TRUE CACHE BOOL "Enables scripting")
-set(SCRIPTING_ENABLED OFF CACHE BOOL "Internal global cmake variable" FORCE)
-
+# Use target folders for MSVC/Xcode/etc.
+include(DeprecationHelpers)
 include(ObsHelpers)
-include(ObsCpack)
-include(GNUInstallDirs)
-include(CheckCCompilerFlag)
-include(CheckCXXCompilerFlag)
-
-set(OBS_RELEASE_CANDIDATE_MAJOR 0)
-set(OBS_RELEASE_CANDIDATE_MINOR 0)
-set(OBS_RELEASE_CANDIDATE_PATCH 0)
-set(OBS_RELEASE_CANDIDATE 0)
-set(OBS_BETA_MAJOR 0)
-set(OBS_BETA_MINOR 0)
-set(OBS_BETA_PATCH 0)
-set(OBS_BETA 0)
-
-# Must be a string in the format of "x.x.x-rcx" or "x.x.x-betax"
-if(DEFINED RELEASE_CANDIDATE)
-	set(OBS_VERSION "${RELEASE_CANDIDATE}")
-	string(REPLACE "-rc" "." RC_SPLIT ${RELEASE_CANDIDATE})
-	string(REPLACE "." ";" RC_SPLIT ${RC_SPLIT})
-	message(WARNING "******************************************************************************\nRelease candidate detected, OBS_VERSION is now: ${OBS_VERSION}\n******************************************************************************")
-	list(GET RC_SPLIT 0 OBS_RELEASE_CANDIDATE_MAJOR)
-	list(GET RC_SPLIT 1 OBS_RELEASE_CANDIDATE_MINOR)
-	list(GET RC_SPLIT 2 OBS_RELEASE_CANDIDATE_PATCH)
-	list(GET RC_SPLIT 3 OBS_RELEASE_CANDIDATE)
-elseif(DEFINED BETA)
-	set(OBS_VERSION "${BETA}")
-	string(REPLACE "-beta" "." BETA_SPLIT ${BETA})
-	string(REPLACE "." ";" BETA_SPLIT ${BETA_SPLIT})
-	message(WARNING "******************************************************************************\nBeta detected, OBS_VERSION is now: ${OBS_VERSION}\n******************************************************************************")
-	list(GET BETA_SPLIT 0 OBS_BETA_MAJOR)
-	list(GET BETA_SPLIT 1 OBS_BETA_MINOR)
-	list(GET BETA_SPLIT 2 OBS_BETA_PATCH)
-	list(GET BETA_SPLIT 3 OBS_BETA)
-endif()
-
-# Binary Versioning for Windows
-if(WIN32)
-	string(REPLACE "-" ";" UI_VERSION_SPLIT ${OBS_VERSION})
-	list(GET UI_VERSION_SPLIT 0 UI_VERSION)
-	string(REPLACE "." ";" UI_VERSION_SEMANTIC ${UI_VERSION})
-	list(GET UI_VERSION_SEMANTIC 0 UI_VERSION_MAJOR)
-	list(GET UI_VERSION_SEMANTIC 1 UI_VERSION_MINOR)
-	list(GET UI_VERSION_SEMANTIC 2 UI_VERSION_PATCH)
-	set(OBS_COMPANY_NAME "OBS")
-	set(OBS_PRODUCT_NAME "OBS Studio")
-	set(OBS_COMMENTS "Free and open source software for video recording and live streaming")
-	set(OBS_LEGAL_COPYRIGHT "(C) Hugh Bailey")
-	set(MODULE_DESCRIPTION "OBS Studio")
-	configure_file(UI/obs.rc.in ${PROJECT_BINARY_DIR}/obs.rc)
-endif()
-
-if(MSVC AND NOT EXISTS "${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.user")
-	file(GENERATE
-		OUTPUT "${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.user"
-		INPUT "${CMAKE_SOURCE_DIR}/cmake/ALL_BUILD.vcxproj.user.in")
-endif()
+# Set default compiler flags
+include(CompilerConfig)
 
+# Allow selection of common build types via UI
 if(NOT CMAKE_BUILD_TYPE)
-        set(CMAKE_BUILD_TYPE RelWithDebInfo)
-endif()
-
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_REQUIRED YES)
-set(CMAKE_CXX_EXTENSIONS NO)
-
-if(${CMAKE_C_COMPILER_ID} MATCHES "Clang" OR ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
-	set(CMAKE_COMPILER_IS_CLANG TRUE)
+  set(CMAKE_BUILD_TYPE
+      "RelWithDebInfo"
+      CACHE STRING
+            "OBS build type [Release, RelWithDebInfo, Debug, MinSizeRel]" FORCE)
+  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Release RelWithDebInfo
+                                               Debug MinSizeRel)
 endif()
 
-if (MSVC_CXX_ARCHITECTURE_ID)
-    string(TOLOWER ${MSVC_CXX_ARCHITECTURE_ID} LOWERCASE_CMAKE_SYSTEM_PROCESSOR)
-else ()
-    string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} LOWERCASE_CMAKE_SYSTEM_PROCESSOR)
-endif ()
-
-if(LOWERCASE_CMAKE_SYSTEM_PROCESSOR MATCHES "(i[3-6]86|x86|x64|x86_64|amd64|e2k)")
-	if(NOT MSVC)
-		set(ARCH_SIMD_FLAGS "-mmmx" "-msse" "-msse2")
-	endif()
-elseif(LOWERCASE_CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64(le)?")
-	set(ARCH_SIMD_DEFINES "-DNO_WARN_X86_INTRINSICS")
-	set(ARCH_SIMD_FLAGS "-mvsx")
-	add_compile_definitions(NO_WARN_X86_INTRINSICS)
-else()
-	if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
-		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSIMDE_ENABLE_OPENMP")
-		set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSIMDE_ENABLE_OPENMP")
-		CHECK_C_COMPILER_FLAG("-fopenmp-simd" C_COMPILER_SUPPORTS_OPENMP_SIMD)
-		if(C_COMPILER_SUPPORTS_OPENMP_SIMD)
-			set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp-simd")
-		endif()
-		CHECK_CXX_COMPILER_FLAG("-fopenmp-simd" CXX_COMPILER_SUPPORTS_OPENMP_SIMD)
-		if(CXX_COMPILER_SUPPORTS_OPENMP_SIMD)
-			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp-simd")
-		endif()
-	endif()
-	set(ARCH_SIMD_FLAGS "")
-	message(STATUS "No Native SSE2 SIMD Support - Using SIMDE")
+# Global project options
+option(BUILD_FOR_DISTRIBUTION "Build for distribution (enables optimisations)"
+       OFF)
+option(ENABLE_UI "Enable building with UI (requires Qt)" ON)
+option(ENABLE_SCRIPTING "Enable scripting support" ON)
+option(USE_LIBCXX "Use libc++ instead of libstdc++" ${APPLE})
+option(
+  BUILD_TESTS
+  "Build test directory (includes test sources and possibly a platform test executable)"
+  OFF)
+
+if(OS_WINDOWS)
+  option(
+    INSTALLER_RUN
+    "Build a multiarch installer (needs to run independently after both archs have compiled) (Windows)"
+    OFF)
+
+elseif(OS_MACOS)
+  option(ENABLE_SPARKLE_UPDATER "Enable Sparkle framework for updates (macOS)"
+         OFF)
+
+elseif(OS_POSIX)
+  option(LINUX_PORTABLE "Build portable version (Linux)" OFF)
+  option(USE_XDG "Utilize XDG Base Directory Specification (Linux)" ON)
+  if(OS_LINUX)
+    option(ENABLE_WAYLAND "Enable building with support for Wayland (Linux)" ON)
+    option(BUILD_FOR_PPA "Build for PPA distribution" OFF)
+  endif()
 endif()
 
-if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG)
-	set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wvla -Wno-unused-function -Wno-missing-field-initializers ${CMAKE_CXX_FLAGS} -fno-strict-aliasing")
-	set(CMAKE_C_FLAGS "-Wall -Wextra -Wvla -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-braces -Wno-missing-field-initializers ${CMAKE_C_FLAGS} -std=gnu99 -fno-strict-aliasing")
-
-	option(USE_LIBC++ "Use libc++ instead of libstdc++" ${APPLE})
-	if(USE_LIBC++)
-		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
-	endif()
-elseif(MSVC)
-	if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
-		string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
-	else()
-		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
-	endif()
+setup_obs_project()
+mark_as_advanced(BUILD_TESTS USE_LIBCXX)
 
-	# Disable pointless constant condition warnings
-	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4127 /wd4201 /wd4456 /wd4457 /wd4458 /wd4459 /wd4595")
+if(INSTALLER_RUN)
+  generate_multiarch_installer()
+  return()
 endif()
 
-if(WIN32)
-	add_definitions(-DUNICODE -D_UNICODE -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS)
+# OBS sources and plugins
+add_subdirectory(deps)
+add_subdirectory(libobs-opengl)
+if(OS_WINDOWS)
+  add_subdirectory(libobs-d3d11)
+  add_subdirectory(libobs-winrt)
 endif()
+add_subdirectory(libobs)
+add_subdirectory(plugins)
 
-if(MSVC)
-	add_compile_options("/MP")
-	set(CMAKE_C_FLAGS_DEBUG "/DDEBUG=1 /D_DEBUG=1 ${CMAKE_C_FLAGS_DEBUG}")
-	set(CMAKE_CXX_FLAGS_DEBUG "/DDEBUG=1 /D_DEBUG=1 ${CMAKE_C_FLAGS_DEBUG}")
-
-	# https://gitlab.kitware.com/cmake/cmake/-/issues/20812
-	set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /Ob2")
-	set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Ob2")
+# OBS main app
+add_subdirectory(UI)
 
-	if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
-		set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
-		set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO")
-		set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO")
-	endif()
-
-	foreach(t EXE SHARED MODULE)
-		set(CMAKE_${t}_LINKER_FLAGS "${CMAKE_${t}_LINKER_FLAGS} /OPT:REF")
-		set(CMAKE_${t}_LINKER_FLAGS_DEBUG "${CMAKE_${t}_LINKER_FLAGS_DEBUG} /INCREMENTAL:NO")
-		set(CMAKE_${t}_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_${t}_LINKER_FLAGS_RELWITHDEBINFO} /INCREMENTAL:NO /OPT:ICF")
-	endforeach()
-else()
-	if(MINGW)
-		set(CMAKE_WIDL "widl" CACHE STRING "wine IDL header file generation program")
-		set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_WIN32_WINNT=0x0600 -DWINVER=0x0600")
-		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WIN32_WINNT=0x0600 -DWINVER=0x0600")
-	endif()
-	set(CMAKE_C_FLAGS_DEBUG "-DDEBUG=1 -D_DEBUG=1 ${CMAKE_C_FLAGS_DEBUG}")
-	set(CMAKE_CXX_FLAGS_DEBUG "-DDEBUG=1 -D_DEBUG=1 ${CMAKE_C_FLAGS_DEBUG}")
+# Tests
+if(ENABLE_UNIT_TESTS)
+  enable_testing()
 endif()
 
-if(APPLE)
-	set(CMAKE_MACOSX_RPATH TRUE)
-	set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
-	list(APPEND CMAKE_INSTALL_RPATH "@loader_path/" "@executable_path/")
-elseif(UNIX)
-	option(USE_XDG "Utilize XDG Base Directory Specification" ON)
-	option(ENABLE_WAYLAND "Build support for Wayland" ON)
-	if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
-		option(ENABLE_DARRAY_TYPE_TEST "Test types of darray argument" ON)
-	else()
-		option(ENABLE_DARRAY_TYPE_TEST "Test types of darray argument" OFF)
-	endif()
-
-	if(USE_XDG)
-		add_definitions(-DUSE_XDG)
-	endif()
-
-	if(NOT UNIX_STRUCTURE)
-		list(APPEND CMAKE_INSTALL_RPATH "$ORIGIN")
-	endif()
-
-	if(ENABLE_DARRAY_TYPE_TEST)
-		add_definitions(-DENABLE_DARRAY_TYPE_TEST)
-	endif()
-endif()
-
-if(LOWERCASE_CMAKE_SYSTEM_PROCESSOR MATCHES "e2k")
-	foreach(TEST_C_FLAG "-Wno-unused-parameter" "-Wno-ignored-qualifiers" "-Wno-pointer-sign" "-Wno-unused-variable" "-Wno-sign-compare" "-Wno-bad-return-value-type" "-Wno-maybe-uninitialized")
-		CHECK_C_COMPILER_FLAG(${TEST_C_FLAG} C_COMPILER_SUPPORTS_FLAG_${TEST_C_FLAG})
-		if(C_COMPILER_SUPPORTS_FLAG_${TEST_C_FLAG})
-			set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TEST_C_FLAG}")
-		endif()
-	endforeach()
-	foreach(TEST_CXX_FLAG "-Wno-invalid-offsetof" "-Wno-maybe-uninitialized")
-		CHECK_CXX_COMPILER_FLAG(${TEST_CXX_FLAG} CXX_COMPILER_SUPPORTS_FLAG_${TEST_CXX_FLAG})
-		if(CXX_COMPILER_SUPPORTS_FLAG_${TEST_CXX_FLAG})
-			set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TEST_CXX_FLAG}")
-		endif()
-	endforeach()
+if(BUILD_TESTS OR ENABLE_UNIT_TESTS)
+  add_subdirectory(test)
 endif()
-
-option(BUILD_TESTS "Build test directory (includes test sources and possibly a platform test executable)" FALSE)
-mark_as_advanced(BUILD_TESTS)
-
-if(NOT INSTALLER_RUN)
-	option(ENABLE_UI "Enables the OBS user interfaces" ON)
-	if(DISABLE_UI OR NOT ENABLE_UI)
-		set(UI_ENABLED FALSE)
-	else()
-		set(UI_ENABLED TRUE)
-
-		if(CMAKE_SIZEOF_VOID_P EQUAL 8)
-			set(_lib_suffix 64)
-		else()
-			set(_lib_suffix 32)
-		endif()
-
-		if(DEFINED QTDIR${_lib_suffix})
-			list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_lib_suffix}}")
-		elseif(DEFINED QTDIR)
-			list(APPEND CMAKE_PREFIX_PATH "${QTDIR}")
-		elseif(DEFINED ENV{QTDIR${_lib_suffix}})
-			list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR${_lib_suffix}}")
-		elseif(DEFINED ENV{QTDIR})
-			list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR}")
-		endif()
-
-		find_package(Qt5Widgets REQUIRED)
-	endif()
-
-	add_subdirectory(deps)
-
-	if(WIN32)
-		add_subdirectory(libobs-d3d11)
-		add_subdirectory(libobs-winrt)
-	endif()
-
-	add_subdirectory(libobs-opengl)
-	add_subdirectory(libobs)
-	add_subdirectory(plugins)
-	add_subdirectory(UI)
-
-	if (ENABLE_UNIT_TESTS)
-		enable_testing()
-	endif()
-
-	if (BUILD_TESTS OR ENABLE_UNIT_TESTS)
-		add_subdirectory(test)
-	endif()
-else()
-	obs_generate_multiarch_installer()
-endif()
-
-include(CopyMSVCBins)

+ 23 - 25
cmake/Modules/CompilerConfig.cmake

@@ -16,38 +16,36 @@ if(OS_WINDOWS AND MSVC)
   endif()
 
   # Check for Win SDK version 10.0.20348 or above
-  if(MSVC_VERSION LESS 1920)
-    message(
-      INFO
-      "  + OBS-Studio - Windows API version is ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}"
-    )
-    string(REPLACE "." ";" WINAPI_VER
-                   "${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
+  message(
+    INFO
+    "  + OBS-Studio - Windows API version is ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}"
+  )
+  string(REPLACE "." ";" WINAPI_VER
+                 "${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
 
-    list(GET WINAPI_VER 0 WINAPI_VER_MAJOR)
-    list(GET WINAPI_VER 1 WINAPI_VER_MINOR)
-    list(GET WINAPI_VER 2 WINAPI_VER_BUILD)
+  list(GET WINAPI_VER 0 WINAPI_VER_MAJOR)
+  list(GET WINAPI_VER 1 WINAPI_VER_MINOR)
+  list(GET WINAPI_VER 2 WINAPI_VER_BUILD)
 
-    set(WINAPI_COMPATIBLE FALSE)
-    if(WINAPI_VER_MAJOR EQUAL 10)
-      if(WINAPI_VER_MINOR EQUAL 0)
-        if(WINAPI_VER_BUILD GREATER_EQUAL 20348)
-          set(WINAPI_COMPATIBLE TRUE)
-        endif()
-      else()
+  set(WINAPI_COMPATIBLE FALSE)
+  if(WINAPI_VER_MAJOR EQUAL 10)
+    if(WINAPI_VER_MINOR EQUAL 0)
+      if(WINAPI_VER_BUILD GREATER_EQUAL 20348)
         set(WINAPI_COMPATIBLE TRUE)
       endif()
-    elseif(WINAPI_VER_MAJOR GREATER 10)
+    else()
       set(WINAPI_COMPATIBLE TRUE)
     endif()
+  elseif(WINAPI_VER_MAJOR GREATER 10)
+    set(WINAPI_COMPATIBLE TRUE)
+  endif()
 
-    if(NOT WINAPI_COMPATIBLE)
-      message(
-        FATAL_ERROR
-          "OBS: OBS requires Windows 10 SDK version 10.0.20348.0 and above to compile.\n"
-          "     Please download the most recent Windows 10 SDK in order to compile (or update to Visual Studio 2019)."
-      )
-    endif()
+  if(NOT WINAPI_COMPATIBLE)
+    message(
+      FATAL_ERROR
+        "OBS: OBS requires Windows 10 SDK version 10.0.20348.0 and above to compile.\n"
+        "     Please download the most recent Windows 10 SDK in order to compile."
+    )
   endif()
 
   add_compile_options(

+ 0 - 43
formatcode.sh

@@ -1,43 +0,0 @@
-#!/usr/bin/env bash
-# Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh
-
-set +x
-set -o errexit
-set -o pipefail
-set -o nounset
-
-# Runs the Clang Formatter in parallel on the code base.
-# Return codes:
-#  - 1 there are files to be formatted
-#  - 0 everything looks fine
-
-# Get CPU count
-OS=$(uname)
-NPROC=1
-if [[ $OS = "Linux" || $OS = "Darwin" ]] ; then
-    NPROC=$(getconf _NPROCESSORS_ONLN)
-fi
-
-# Discover clang-format
-if type clang-format-12 2> /dev/null ; then
-    CLANG_FORMAT=clang-format-12
-elif type clang-format-10 2> /dev/null ; then
-    CLANG_FORMAT=clang-format-10
-elif type clang-format-8 2> /dev/null ; then
-    CLANG_FORMAT=clang-format-8
-else
-    CLANG_FORMAT=clang-format
-fi
-
-find . -type d \( -path ./deps \
--o -path ./cmake \
--o -path ./plugins/decklink/win/decklink-sdk \
--o -path ./plugins/decklink/mac/decklink-sdk \
--o -path ./plugins/decklink/linux/decklink-sdk \
--o -path ./plugins/enc-amf \
--o -path ./plugins/mac-syphon/syphon-framework \
--o -path ./plugins/obs-outputs/ftl-sdk \
--o -path ./plugins/obs-vst \
--o -path ./plugins/aja/sdk \
--o -path ./build \) -prune -type f -o -name '*.h' -or -name '*.hpp' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \
-| xargs -L100 -P${NPROC} ${CLANG_FORMAT} -i -style=file  -fallback-style=none

+ 9 - 10
test/CMakeLists.txt

@@ -1,16 +1,15 @@
-
 if(BUILD_TESTS)
-	add_subdirectory(test-input)
+  add_subdirectory(test-input)
 
-	if(WIN32)
-		add_subdirectory(win)
-	endif()
+  if(OS_WINDOWS)
+    add_subdirectory(win)
+  endif()
 
-	if(APPLE AND UNIX)
-		add_subdirectory(osx)
-	endif()
+  if(OS_POSIX)
+    add_subdirectory(osx)
+  endif()
 endif()
 
-if (ENABLE_UNIT_TESTS)
-	add_subdirectory(cmocka)
+if(ENABLE_UNIT_TESTS)
+  add_subdirectory(cmocka)
 endif()

+ 6 - 33
test/cmocka/CMakeLists.txt

@@ -1,50 +1,23 @@
 project(obs-cmocka)
 
-# Fix libobs path
-macro(fixLink target_arg)
-	if(APPLE AND UNIX)
-		add_custom_command (TARGET ${target_arg}
-			POST_BUILD COMMAND "${CMAKE_INSTALL_NAME_TOOL}"
-			"-change" "@rpath/libobs.0.dylib" "@executable_path/../../libobs/libobs.0.dylib"
-			"$<TARGET_FILE:${target_arg}>" VERBATIM)
-		add_custom_command (TARGET ${target_arg}
-			POST_BUILD COMMAND "${CMAKE_INSTALL_NAME_TOOL}"
-			"-add_rpath" /tmp/obsdeps/lib
-			"$<TARGET_FILE:${target_arg}>" VERBATIM)
-	endif()
-endmacro()
-
-set(CMAKE_MACOSX_RPATH TRUE)
-set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
-list(APPEND CMAKE_INSTALL_RPATH "@loader_path/" "@executable_path/")
-
 find_package(CMocka CONFIG REQUIRED)
-include_directories(${CMOCKA_INCLUDE_DIR})
-include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs")
-
-# fix rpath on linux
-if (UNIX AND NOT APPLE)
-	set(CMAKE_INSTALL_RPATH "$ORIGIN";../../libobs)
-endif()
 
 # Serializer test
 add_executable(test_serializer test_serializer.c)
-target_link_libraries(test_serializer ${CMOCKA_LIBRARIES} libobs)
-
+target_include_directories(test_serializer PRIVATE ${CMOCKA_INCLUDE_DIR})
+target_link_libraries(test_serializer PRIVATE OBS::libobs ${CMOCKA_LIBRARIES})
 add_test(test_serializer ${CMAKE_CURRENT_BINARY_DIR}/test_serializer)
-fixLink(test_serializer)
-
 
 # darray test
 add_executable(test_darray test_darray.c)
-target_link_libraries(test_darray ${CMOCKA_LIBRARIES} libobs)
+target_include_directories(test_darray PRIVATE ${CMOCKA_INCLUDE_DIR})
+target_link_libraries(test_darray PRIVATE OBS::libobs ${CMOCKA_LIBRARIES})
 
 add_test(test_darray ${CMAKE_CURRENT_BINARY_DIR}/test_darray)
-fixLink(test_darray)
 
 # bitstream test
 add_executable(test_bitstream test_bitstream.c)
-target_link_libraries(test_bitstream ${CMOCKA_LIBRARIES} libobs)
+target_include_directories(test_bitstream PRIVATE ${CMOCKA_INCLUDE_DIR})
+target_link_libraries(test_bitstream PRIVATE OBS::libobs ${CMOCKA_LIBRARIES})
 
 add_test(test_bitstream ${CMAKE_CURRENT_BINARY_DIR}/test_bitstream)
-fixLink(test_bitstream)

+ 12 - 13
test/osx/CMakeLists.txt

@@ -1,19 +1,18 @@
 project(osx-text)
 
-include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs")
-
 find_library(COCOA Cocoa)
-include_directories(${COCOA})
+mark_as_advanced(COCOA)
+
+add_executable(macOS_test)
+
+target_sources(macOS_test PRIVATE test.mm)
+
+target_link_libraries(macOS_test PRIVATE OBS::libobs ${COCOA})
+
+target_compile_options(macOS_test PRIVATE -fobjc-arc)
 
-add_definitions(-fobjc-arc)
+target_compile_features(macOS_test PRIVATE cxx_std_11)
 
-set(osx-test_SOURCES
-	test.mm)
+set_target_properties(macOS_test PROPERTIES FOLDER "tests and examples")
 
-add_executable(osx_test
-	${osx-test_SOURCES})
-set_target_properties(osx_test PROPERTIES FOLDER "tests and examples")
-target_link_libraries(osx_test
-	libobs
-	${COCOA})
-define_graphic_modules(osx_test)
+define_graphic_modules(macOS_test)

+ 16 - 21
test/test-input/CMakeLists.txt

@@ -1,29 +1,24 @@
 project(test-input)
 
-include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs")
+add_library(test-input MODULE)
 
-if(MSVC)
-	set(test-input_PLATFORM_DEPS
-		w32-pthreads)
-endif()
+target_sources(
+  test-input
+  PRIVATE test-filter.c
+          test-input.c
+          test-sinewave.c
+          sync-async-source.c
+          sync-audio-buffering.c
+          sync-pair-vid.c
+          sync-pair-aud.c
+          test-random.c)
 
-set(test-input_SOURCES
-	${test-input_PLATFORM_SOURCES}
-	test-filter.c
-	test-input.c
-	test-sinewave.c
-	sync-async-source.c
-	sync-audio-buffering.c
-	sync-pair-vid.c
-	sync-pair-aud.c
-	test-random.c)
+target_link_libraries(test-input PRIVATE OBS::libobs)
 
-add_library(test-input MODULE
-	${test-input_SOURCES})
+if(MSVC)
+  target_link_libraries(test-input PRIVATE OBS::w32-pthreads)
+endif()
 
-target_link_libraries(test-input
-	${test-input_PLATFORM_DEPS}
-	libobs)
 set_target_properties(test-input PROPERTIES FOLDER "tests and examples")
 
-install_obs_plugin_with_data(test-input data)
+setup_plugin_target(test-input)

+ 5 - 7
test/win/CMakeLists.txt

@@ -1,13 +1,11 @@
 project(win-test)
 
-include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs")
+add_executable(win-test WIN32)
 
-set(win-text_SOURCES
-	test.cpp)
+target_sources(win-test PRIVATE test.cpp)
+
+target_link_libraries(win-test PRIVATE OBS::libobs)
 
-add_executable(win-test WIN32
-	${win-text_SOURCES})
-target_link_libraries(win-test
-	libobs)
 set_target_properties(win-test PROPERTIES FOLDER "tests and examples")
+
 define_graphic_modules(win-test)