build: assemble pinned OBS 32.0.4 runtimes for Linux and Windows
Linux builds libobs and an allowlist of official plugins from source in OBS's relocatable layout. Windows copies the official release byte-for-byte so the Game Capture hook keeps its signature. Adds a headless Wayland rig for PipeWire/portal capture tests on machines without a display.
This commit is contained in:
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
# Assemble the pinned OBS runtime for Windows x64 from the official release zip.
|
||||
#
|
||||
# Files are copied byte-for-byte from the OBS release so the signed Game
|
||||
# Capture hook (graphics-hook64.dll, inject-helper, get-graphics-offsets) keeps
|
||||
# its original signature. Runs on Linux or Git Bash; needs curl, unzip and
|
||||
# sha256sum.
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=versions.env
|
||||
source "${script_dir}/versions.env"
|
||||
|
||||
cache_dir="${CLIPBIN_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/clipbin}"
|
||||
zip_name="OBS-Studio-${OBS_VERSION}-Windows-x64.zip"
|
||||
zip_path="${cache_dir}/${zip_name}"
|
||||
extract_dir="${cache_dir}/obs-windows-${OBS_VERSION}"
|
||||
bundle_dir="${CLIPBIN_OBS_RUNTIME:-${script_dir}/../../target/obs-runtime/windows-x86_64}"
|
||||
|
||||
# Official plugins the recorder uses: Game/Window Capture, WASAPI audio,
|
||||
# hardware encoders (AMF lives in obs-ffmpeg) and the FFmpeg muxer.
|
||||
plugins=(win-capture win-wasapi obs-ffmpeg obs-nvenc obs-qsv11)
|
||||
# OBS front-end, scripting and software-encoder files Clipbin does not use.
|
||||
# Subdirectories of bin/64bit are Qt plugins and are skipped too.
|
||||
excluded_bin='^(obs64\.exe|obs-frontend-api\.dll|obs-scripting\.dll|lua51\.dll|Qt6.*\.dll|datachannel\.dll|libx264-.*\.dll|.*\.pdb)$'
|
||||
|
||||
mkdir -p "${cache_dir}"
|
||||
if [[ ! -f "${zip_path}" ]]; then
|
||||
curl -fL --retry 3 -o "${zip_path}.partial" \
|
||||
"https://github.com/obsproject/obs-studio/releases/download/${OBS_VERSION}/${zip_name}"
|
||||
mv "${zip_path}.partial" "${zip_path}"
|
||||
fi
|
||||
echo "${OBS_WINDOWS_X64_SHA256} ${zip_path}" | sha256sum --check --quiet
|
||||
|
||||
rm -rf "${extract_dir}"
|
||||
unzip -q "${zip_path}" -d "${extract_dir}"
|
||||
|
||||
# Keep the OBS install layout: OBS looks for obs-ffmpeg-mux.exe next to the
|
||||
# running executable, so Clipbin's executable lives in bin/64bit.
|
||||
rm -rf "${bundle_dir}"
|
||||
mkdir -p "${bundle_dir}/bin/64bit" "${bundle_dir}/obs-plugins/64bit" "${bundle_dir}/data/obs-plugins"
|
||||
for file in "${extract_dir}"/bin/64bit/*; do
|
||||
name="$(basename "${file}")"
|
||||
if [[ -f "${file}" && ! "${name}" =~ ${excluded_bin} ]]; then
|
||||
cp -a "${file}" "${bundle_dir}/bin/64bit/"
|
||||
fi
|
||||
done
|
||||
cp -a "${extract_dir}/data/libobs" "${bundle_dir}/data/libobs"
|
||||
for plugin in "${plugins[@]}"; do
|
||||
cp -a "${extract_dir}/obs-plugins/64bit/${plugin}.dll" "${bundle_dir}/obs-plugins/64bit/"
|
||||
if [[ -d "${extract_dir}/data/obs-plugins/${plugin}" ]]; then
|
||||
cp -a "${extract_dir}/data/obs-plugins/${plugin}" "${bundle_dir}/data/obs-plugins/"
|
||||
fi
|
||||
done
|
||||
find "${bundle_dir}" -name '*.pdb' -delete
|
||||
|
||||
# Record the signed capture-hook files so a Windows check can verify them
|
||||
# against Get-AuthenticodeSignature before packaging.
|
||||
(cd "${bundle_dir}" && sha256sum data/obs-plugins/win-capture/*.exe data/obs-plugins/win-capture/*.dll) \
|
||||
> "${bundle_dir}/SIGNED-HOOKS.sha256"
|
||||
|
||||
cat > "${bundle_dir}/VERSION" <<EOF
|
||||
obs=${OBS_VERSION}
|
||||
source=${zip_name}
|
||||
sha256=${OBS_WINDOWS_X64_SHA256}
|
||||
plugins=${plugins[*]}
|
||||
EOF
|
||||
echo "OBS ${OBS_VERSION} Windows runtime assembled in ${bundle_dir}"
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the pinned OBS runtime for Linux and assemble the bundle Clipbin ships.
|
||||
#
|
||||
# Only libobs and an allowlist of official OBS plugins enter the bundle. The
|
||||
# Windows bundle uses the official OBS release binaries instead (see
|
||||
# scripts/obs/fetch-windows.ps1) so the signed Game Capture hooks stay intact.
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=versions.env
|
||||
source "${script_dir}/versions.env"
|
||||
|
||||
cache_dir="${CLIPBIN_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/clipbin}"
|
||||
source_dir="${cache_dir}/obs-studio-${OBS_VERSION}"
|
||||
build_dir="${cache_dir}/obs-build-${OBS_VERSION}"
|
||||
stage_dir="${cache_dir}/obs-stage-${OBS_VERSION}"
|
||||
bundle_dir="${CLIPBIN_OBS_RUNTIME:-${script_dir}/../../target/obs-runtime/linux-x86_64}"
|
||||
|
||||
# Official plugins the recorder uses: capture, audio, hardware encoders and the
|
||||
# FFmpeg muxer. obs-outputs (RTMP, native MP4) is built but not shipped.
|
||||
plugins=(linux-capture linux-pipewire linux-pulseaudio obs-ffmpeg obs-nvenc)
|
||||
|
||||
# OBS 32.0 does not build against mbedtls 4. Arch ships 3.x as mbedtls3.
|
||||
extra_cmake_args=()
|
||||
if [[ -d /usr/lib/mbedtls3/cmake/MbedTLS ]]; then
|
||||
extra_cmake_args+=(-DMbedTLS_DIR=/usr/lib/mbedtls3/cmake/MbedTLS)
|
||||
fi
|
||||
|
||||
if [[ ! -d "${source_dir}" ]]; then
|
||||
git clone --depth 1 --branch "${OBS_VERSION}" --recurse-submodules --shallow-submodules \
|
||||
https://github.com/obsproject/obs-studio.git "${source_dir}"
|
||||
fi
|
||||
|
||||
actual_commit="$(git -C "${source_dir}" rev-parse HEAD)"
|
||||
if [[ "${actual_commit}" != "${OBS_COMMIT}" ]]; then
|
||||
echo "OBS source is at ${actual_commit}, expected ${OBS_COMMIT} (${OBS_VERSION})" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cmake -S "${source_dir}" -B "${build_dir}" -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INSTALL_PREFIX="${stage_dir}" \
|
||||
-DENABLE_RELOCATABLE=ON \
|
||||
-DENABLE_PORTABLE_CONFIG=ON \
|
||||
-DENABLE_FRONTEND=OFF \
|
||||
-DENABLE_UI=OFF \
|
||||
-DENABLE_BROWSER=OFF \
|
||||
-DENABLE_WEBSOCKET=OFF \
|
||||
-DENABLE_WEBRTC=OFF \
|
||||
-DENABLE_SCRIPTING=OFF \
|
||||
-DENABLE_VLC=OFF \
|
||||
-DENABLE_AJA=OFF \
|
||||
-DENABLE_DECKLINK=OFF \
|
||||
-DENABLE_VST=OFF \
|
||||
-DENABLE_QSV11=OFF \
|
||||
-DENABLE_LIBFDK=OFF \
|
||||
-DENABLE_JACK=OFF \
|
||||
-DENABLE_SNDIO=OFF \
|
||||
-DENABLE_OSS=OFF \
|
||||
-DENABLE_V4L2=OFF \
|
||||
-DENABLE_VIRTUALCAM=OFF \
|
||||
-DENABLE_NEW_MPEGTS_OUTPUT=OFF \
|
||||
-DENABLE_PIPEWIRE=ON \
|
||||
-DENABLE_WAYLAND=ON \
|
||||
-DENABLE_PULSEAUDIO=ON \
|
||||
-DENABLE_NVENC=ON \
|
||||
-DENABLE_HEVC=ON \
|
||||
"${extra_cmake_args[@]}"
|
||||
cmake --build "${build_dir}"
|
||||
rm -rf "${stage_dir}"
|
||||
cmake --install "${build_dir}"
|
||||
|
||||
# The bundle keeps OBS's relocatable install layout: binaries in bin/ (OBS
|
||||
# looks for obs-ffmpeg-mux next to the running executable), libraries in lib/,
|
||||
# and plugins in lib/obs-plugins/ so their $ORIGIN/.. runpath resolves libobs.
|
||||
rm -rf "${bundle_dir}"
|
||||
mkdir -p "${bundle_dir}/bin" "${bundle_dir}/lib/obs-plugins" "${bundle_dir}/lib/pkgconfig" \
|
||||
"${bundle_dir}/share/obs/obs-plugins"
|
||||
cp -a "${stage_dir}/bin/obs-ffmpeg-mux" "${bundle_dir}/bin/"
|
||||
cp -a "${stage_dir}"/lib/libobs.so* "${stage_dir}"/lib/libobs-opengl.so* "${bundle_dir}/lib/"
|
||||
cp -a "${stage_dir}/share/obs/libobs" "${bundle_dir}/share/obs/libobs"
|
||||
for plugin in "${plugins[@]}"; do
|
||||
cp -a "${stage_dir}/lib/obs-plugins/${plugin}.so" "${bundle_dir}/lib/obs-plugins/"
|
||||
if [[ -d "${stage_dir}/share/obs/obs-plugins/${plugin}" ]]; then
|
||||
cp -a "${stage_dir}/share/obs/obs-plugins/${plugin}" "${bundle_dir}/share/obs/obs-plugins/"
|
||||
fi
|
||||
done
|
||||
# Lets the libobs crate link against this runtime instead of a system OBS.
|
||||
sed -e 's|^prefix=.*|prefix=${pcfiledir}/../..|' \
|
||||
"${stage_dir}/lib/pkgconfig/libobs.pc" > "${bundle_dir}/lib/pkgconfig/libobs.pc"
|
||||
|
||||
cat > "${bundle_dir}/VERSION" <<EOF
|
||||
obs=${OBS_VERSION}
|
||||
commit=${OBS_COMMIT}
|
||||
plugins=${plugins[*]}
|
||||
EOF
|
||||
echo "OBS ${OBS_VERSION} runtime assembled in ${bundle_dir}"
|
||||
@@ -0,0 +1,5 @@
|
||||
# The tested OBS version set. The libobs crate's headers must match OBS_VERSION.
|
||||
OBS_VERSION=32.0.4
|
||||
OBS_COMMIT=dcdbd2e9048ae66237fdab32dbba72b745b1b9c5
|
||||
# Official release zip; the digest is the one GitHub publishes for the asset.
|
||||
OBS_WINDOWS_X64_SHA256=fd367a7f156319426f1a600720c1288ad0011da168dff41e0f94dbb18cc1dc0f
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run a command inside a disposable headless Wayland desktop for capture tests.
|
||||
#
|
||||
# scripts/rig/headless-wayland.sh <command> [args...]
|
||||
#
|
||||
# Starts a private D-Bus session with PipeWire, WirePlumber, headless Sway and
|
||||
# xdg-desktop-portal-wlr (auto-selecting the only output), plus a 60 FPS test
|
||||
# pattern standing in for a game. Everything is torn down when the command exits.
|
||||
#
|
||||
# This rig exercises the PipeWire/portal path on a machine without a display.
|
||||
# It does not replace validation on real hardware (Hyprland, X11, Windows).
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "usage: $0 <command> [args...]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ -z "${CLIPBIN_RIG_INNER:-}" ]]; then
|
||||
exec env CLIPBIN_RIG_INNER=1 dbus-run-session -- "$0" "$@"
|
||||
fi
|
||||
|
||||
rig_dir="$(mktemp -d "${TMPDIR:-/tmp}/clipbin-rig.XXXXXX")"
|
||||
pids=()
|
||||
cleanup() {
|
||||
for pid in "${pids[@]}"; do kill "${pid}" 2>/dev/null || true; done
|
||||
wait 2>/dev/null || true
|
||||
# The D-Bus-activated document portal mounts a FUSE filesystem here.
|
||||
fusermount3 -u "${XDG_RUNTIME_DIR}/doc" 2>/dev/null || true
|
||||
rm -rf "${rig_dir}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
export XDG_RUNTIME_DIR="${rig_dir}/runtime"
|
||||
export XDG_CONFIG_HOME="${rig_dir}/config"
|
||||
export XDG_CURRENT_DESKTOP=sway
|
||||
export XDG_SESSION_TYPE=wayland
|
||||
mkdir -p "${XDG_RUNTIME_DIR}" "${XDG_CONFIG_HOME}/xdg-desktop-portal-wlr" "${XDG_CONFIG_HOME}/sway"
|
||||
chmod 700 "${XDG_RUNTIME_DIR}"
|
||||
|
||||
width="${CLIPBIN_RIG_WIDTH:-1920}"
|
||||
height="${CLIPBIN_RIG_HEIGHT:-1080}"
|
||||
|
||||
cat > "${XDG_CONFIG_HOME}/sway/config" <<EOF
|
||||
output HEADLESS-1 resolution ${width}x${height}@60Hz
|
||||
default_border none
|
||||
EOF
|
||||
cat > "${XDG_CONFIG_HOME}/xdg-desktop-portal-wlr/config" <<EOF
|
||||
[screencast]
|
||||
chooser_type=none
|
||||
output_name=HEADLESS-1
|
||||
max_fps=60
|
||||
EOF
|
||||
|
||||
wait_for() {
|
||||
local what="$1" check="$2"
|
||||
for _ in $(seq 100); do
|
||||
if eval "${check}"; then return 0; fi
|
||||
sleep 0.1
|
||||
done
|
||||
echo "rig: ${what} did not start; logs in ${rig_dir}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
pipewire > "${rig_dir}/pipewire.log" 2>&1 & pids+=($!)
|
||||
wait_for pipewire '[[ -S "${XDG_RUNTIME_DIR}/pipewire-0" ]]'
|
||||
wireplumber > "${rig_dir}/wireplumber.log" 2>&1 & pids+=($!)
|
||||
pipewire-pulse > "${rig_dir}/pipewire-pulse.log" 2>&1 & pids+=($!)
|
||||
|
||||
WLR_BACKENDS=headless WLR_LIBINPUT_NO_DEVICES=1 WLR_HEADLESS_OUTPUTS=1 \
|
||||
sway --config "${XDG_CONFIG_HOME}/sway/config" > "${rig_dir}/sway.log" 2>&1 & pids+=($!)
|
||||
wait_for sway 'compgen -G "${XDG_RUNTIME_DIR}/wayland-*" > /dev/null'
|
||||
WAYLAND_DISPLAY="$(basename "$(compgen -G "${XDG_RUNTIME_DIR}/wayland-*" | grep -v lock | head -1)")"
|
||||
export WAYLAND_DISPLAY
|
||||
SWAYSOCK="$(compgen -G "${XDG_RUNTIME_DIR}/sway-ipc.*" | head -1)"
|
||||
export SWAYSOCK
|
||||
|
||||
dbus-update-activation-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_RUNTIME_DIR XDG_CONFIG_HOME SWAYSOCK
|
||||
/usr/lib/xdg-desktop-portal-wlr > "${rig_dir}/xdpw.log" 2>&1 & pids+=($!)
|
||||
/usr/lib/xdg-desktop-portal -r > "${rig_dir}/xdp.log" 2>&1 & pids+=($!)
|
||||
|
||||
# A moving 60 FPS picture with a tone, standing in for a game.
|
||||
SDL_VIDEODRIVER=wayland ffplay -loglevel error -fs -window_title clipbin-test-game \
|
||||
-f lavfi "testsrc2=size=${width}x${height}:rate=60,format=yuv420p" \
|
||||
> "${rig_dir}/game.log" 2>&1 & pids+=($!)
|
||||
sleep 1
|
||||
|
||||
echo "rig: ready (WAYLAND_DISPLAY=${WAYLAND_DISPLAY}, logs in ${rig_dir})" >&2
|
||||
status=0
|
||||
"$@" || status=$?
|
||||
if [[ -n "${CLIPBIN_RIG_KEEP_LOGS:-}" ]]; then
|
||||
cp -r "${rig_dir}" "${CLIPBIN_RIG_KEEP_LOGS}"
|
||||
fi
|
||||
exit "${status}"
|
||||
Reference in New Issue
Block a user