Skip to content

Commit 0fa282c

Browse files
kixelatedclaude
andauthored
Portable ffmpeg linkage + build.sh for releases (#43)
* Portable ffmpeg linkage + build.sh for releases Make the plugin link the ffmpeg the obs-deps bundle ships (the same one OBS uses at runtime) on macOS and Windows, so release binaries are portable: - CMakeLists: use find_package(FFmpeg) on WIN32 OR APPLE via a vendored FindFFmpeg.cmake that searches the obs-deps prefix (obs-deps ship ffmpeg with no pkg-config .pc files). Linux keeps pkg-config. Previously macOS used pkg-config (reached for Homebrew ffmpeg at an absolute path) and Windows couldn't find ffmpeg at all. - cmake/common/FindFFmpeg.cmake: vendored finder (no pkg-config dependency). - bootstrap.cmake: honor -DPLUGIN_VERSION_OVERRIDE so release CI can version the plugin to match the libmoq release it links. - cmake/macos/buildspec.cmake: make the xattr quarantine strip non-fatal (it errors on build outputs that carry no quarantine attribute). - build.sh: drive per-platform configure/build/package. --libmoq-release fetches a published libmoq release (no local cargo build) and versions the plugin to match; otherwise the buildspec MOQ_VERSION is used. Verified on macOS arm64: links @rpath/libavcodec.dylib (obs-deps ffmpeg 7), not Homebrew. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * gersemi format CMakeLists + FindFFmpeg Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: install ffmpeg dev on Ubuntu, build macOS arm64-only Two pre-existing CI gaps that blocked the plugin build: - Ubuntu: the .Aptfile installed no ffmpeg dev libraries, so the Linux pkg_check_modules(FFMPEG ...) failed at configure. Add libav{codec,util}-dev and libsw{scale,resample}-dev. - macOS: build-macos forced a universal (arm64 + x86_64) build, but the plugin links a published libmoq release, which ships per-architecture (no universal archive). The x86_64 slice couldn't link the arm64 libmoq. Build arm64 only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * gersemi 0.21.0 format FindFFmpeg.cmake to match CI Local nix gersemi (0.23.2) formats differently than CI's pinned 0.21.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 4dbe6bc commit 0fa282c

7 files changed

Lines changed: 564 additions & 8 deletions

File tree

.github/scripts/.Aptfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,10 @@ package 'git'
44
package 'jq'
55
package 'ninja-build', bin: 'ninja'
66
package 'pkg-config'
7+
8+
# FFmpeg dev libraries: the MoQ source decodes video with libav*, found via
9+
# pkg-config on Linux (macOS/Windows use the obs-deps bundle instead).
10+
package 'libavcodec-dev'
11+
package 'libavutil-dev'
12+
package 'libswscale-dev'
13+
package 'libswresample-dev'

.github/scripts/build-macos

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,12 @@ build() {
126126
}
127127
}
128128
129+
# arm64-only: the plugin links a published libmoq release, which ships
130+
# per-architecture (no universal archive), so a universal plugin can't link
131+
# the x86_64 slice. Build arm64 only to match the fetched libmoq.
129132
local -a build_args=(
130133
ONLY_ACTIVE_ARCH=NO
131134
-arch arm64
132-
-arch x86_64
133135
-project ${product_name}.xcodeproj
134136
-target ${product_name}
135137
-destination "generic/platform=macOS,name=Any Mac"

CMakeLists.txt

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,21 @@ add_library(obs-moq MODULE)
1515

1616
if(${BUILD_PLUGIN})
1717
find_package(libobs REQUIRED)
18-
# FFmpeg dependency
19-
include(FindPkgConfig)
20-
pkg_check_modules(FFMPEG REQUIRED libavcodec libavutil libswscale libswresample)
21-
target_include_directories(obs-moq PRIVATE ${FFMPEG_INCLUDE_DIRS})
22-
target_link_directories(obs-moq PRIVATE ${FFMPEG_LIBRARY_DIRS})
23-
target_link_libraries(obs-moq PRIVATE ${FFMPEG_LIBRARIES})
18+
# FFmpeg dependency (used by the MoQ source to decode subscribed video). On
19+
# macOS and Windows, link the ffmpeg the obs-deps bundle ships -- the same one
20+
# OBS itself uses at runtime -- via the vendored FindFFmpeg finder (obs-deps
21+
# ship ffmpeg with no pkg-config .pc files, so it searches the prefix). On
22+
# Linux there's no obs-deps; pkg-config finds the system ffmpeg.
23+
if(WIN32 OR APPLE)
24+
find_package(FFmpeg REQUIRED avcodec avutil swscale swresample)
25+
target_link_libraries(obs-moq PRIVATE FFmpeg::avcodec FFmpeg::avutil FFmpeg::swscale FFmpeg::swresample)
26+
else()
27+
include(FindPkgConfig)
28+
pkg_check_modules(FFMPEG REQUIRED libavcodec libavutil libswscale libswresample)
29+
target_include_directories(obs-moq PRIVATE ${FFMPEG_INCLUDE_DIRS})
30+
target_link_directories(obs-moq PRIVATE ${FFMPEG_LIBRARY_DIRS})
31+
target_link_libraries(obs-moq PRIVATE ${FFMPEG_LIBRARIES})
32+
endif()
2433
else()
2534
find_package(FFmpeg REQUIRED avcodec avutil swscale swresample)
2635
target_link_libraries(obs-moq PRIVATE FFmpeg::avcodec FFmpeg::avutil FFmpeg::swscale FFmpeg::swresample)

build.sh

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Build and package the obs-moq plugin for release.
5+
# Usage: ./build.sh [--target TARGET] [--version VERSION] [--output DIR]
6+
#
7+
# The required toolchain must already be on PATH; this script only drives
8+
# CMake. Per platform:
9+
# Linux - run inside `nix develop` (provides cmake/ninja/obs-studio/qt6/ffmpeg)
10+
# macOS - full Xcode, run OUTSIDE nix (libobs/Qt6/ffmpeg all come from the
11+
# obs-deps bundle downloaded by buildspec.json at configure time)
12+
# Windows - Visual Studio 2022; run from Git Bash with cmake on PATH
13+
# (libobs/Qt6 downloaded by buildspec.json)
14+
#
15+
# Produces $OUTPUT_DIR/obs-moq-$VERSION-$TARGET.{tar.gz,zip}. The archive is
16+
# unsigned; macOS Gatekeeper / Windows SmartScreen will warn on first load.
17+
18+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19+
20+
TARGET=""
21+
VERSION=""
22+
OUTPUT_DIR="dist"
23+
MOQ_RELEASE=""
24+
25+
while [[ $# -gt 0 ]]; do
26+
case $1 in
27+
--target)
28+
TARGET="$2"
29+
shift 2
30+
;;
31+
--version)
32+
VERSION="$2"
33+
shift 2
34+
;;
35+
--output)
36+
OUTPUT_DIR="$2"
37+
shift 2
38+
;;
39+
--libmoq-release)
40+
# Link a published libmoq release of this version instead of
41+
# building rs/libmoq from source. CMake fetches the matching
42+
# moq-<version>-<target> archive from the GitHub release and the
43+
# plugin is versioned to match. Used by CI on a libmoq-v* tag.
44+
MOQ_RELEASE="$2"
45+
shift 2
46+
;;
47+
-h | --help)
48+
echo "Usage: $0 [--target TARGET] [--version VERSION] [--output DIR] [--libmoq-release VERSION]"
49+
exit 0
50+
;;
51+
*)
52+
echo "Unknown option: $1" >&2
53+
exit 1
54+
;;
55+
esac
56+
done
57+
58+
# In libmoq-release mode the plugin version tracks the libmoq version.
59+
if [[ -n "$MOQ_RELEASE" ]]; then
60+
VERSION="$MOQ_RELEASE"
61+
fi
62+
63+
if [[ -z "$TARGET" ]]; then
64+
TARGET=$(cc -dumpmachine 2>/dev/null || echo unknown)
65+
echo "Detected target: $TARGET"
66+
fi
67+
68+
# Default the version from buildspec.json's top-level "version" (the nested
69+
# dependency entries also have "version" keys, hence the leading-indent anchor).
70+
if [[ -z "$VERSION" ]]; then
71+
VERSION=$(grep -E '^[[:space:]]{4}"version"' "$SCRIPT_DIR/buildspec.json" | head -1 | sed 's/.*: *"\([^"]*\)".*/\1/')
72+
echo "Detected version: $VERSION"
73+
fi
74+
75+
# Map the target triple to a CMake preset and build tree.
76+
case "$TARGET" in
77+
*-linux-*)
78+
PRESET="ubuntu-x86_64"
79+
BUILD_DIR="$SCRIPT_DIR/build_x86_64"
80+
KIND="unix"
81+
;;
82+
*-apple-darwin)
83+
PRESET="macos"
84+
BUILD_DIR="$SCRIPT_DIR/build_macos"
85+
KIND="macos"
86+
;;
87+
*-windows-*)
88+
PRESET="windows-x64"
89+
BUILD_DIR="$SCRIPT_DIR/build_x64"
90+
KIND="windows"
91+
;;
92+
*)
93+
echo "Unsupported target: $TARGET" >&2
94+
exit 1
95+
;;
96+
esac
97+
98+
# Resolve the output dir to an absolute path before we cd into the plugin
99+
# directory (cmake --preset reads CMakePresets.json from the current dir).
100+
mkdir -p "$OUTPUT_DIR"
101+
OUTPUT_DIR="$(cd "$OUTPUT_DIR" && pwd)"
102+
cd "$SCRIPT_DIR"
103+
104+
echo "Building obs-moq $VERSION for $TARGET (preset: $PRESET)..."
105+
CONFIGURE_ARGS=()
106+
# Stamp the plugin's compiled-in version (project version, macOS Info.plist,
107+
# Windows resource) to match what we're building, not buildspec.json's 0.0.1.
108+
if [[ -n "$VERSION" ]]; then
109+
CONFIGURE_ARGS+=("-DPLUGIN_VERSION_OVERRIDE=$VERSION")
110+
fi
111+
if [[ -n "$MOQ_RELEASE" ]]; then
112+
# Empty MOQ_LOCAL forces CMake's release-download branch; MOQ_VERSION and
113+
# MOQ_TARGET steer it at this target's archive (the presets hard-code an
114+
# x86_64/stale default). MOQ_ARCHIVE is correct per preset already.
115+
echo "Linking libmoq release v$MOQ_RELEASE ($TARGET)"
116+
CONFIGURE_ARGS+=(-DMOQ_LOCAL= "-DMOQ_VERSION=$MOQ_RELEASE" "-DMOQ_TARGET=$TARGET")
117+
fi
118+
cmake --preset "$PRESET" ${CONFIGURE_ARGS[@]+"${CONFIGURE_ARGS[@]}"}
119+
cmake --build --preset "$PRESET"
120+
121+
NAME="obs-moq-${VERSION}-${TARGET}"
122+
STAGE="$OUTPUT_DIR/$NAME"
123+
rm -rf "$STAGE"
124+
mkdir -p "$OUTPUT_DIR"
125+
126+
if [[ "$KIND" == "macos" ]]; then
127+
# Self-contained loadable bundle; drop into the OBS plugins directory.
128+
PLUGIN=$(find "$BUILD_DIR" -name 'obs-moq.plugin' -maxdepth 4 -print -quit)
129+
[[ -n "$PLUGIN" ]] || {
130+
echo "obs-moq.plugin not found under $BUILD_DIR" >&2
131+
exit 1
132+
}
133+
mkdir -p "$STAGE"
134+
cp -R "$PLUGIN" "$STAGE/"
135+
else
136+
# OBS portable-plugin layout: extract into your OBS plugins directory.
137+
LIB=$(find "$BUILD_DIR" \( -name 'obs-moq.so' -o -name 'obs-moq.dll' \) -print -quit)
138+
[[ -n "$LIB" ]] || {
139+
echo "obs-moq.{so,dll} not found under $BUILD_DIR" >&2
140+
exit 1
141+
}
142+
mkdir -p "$STAGE/obs-moq/bin/64bit"
143+
cp "$LIB" "$STAGE/obs-moq/bin/64bit/"
144+
cp -R "$SCRIPT_DIR/data" "$STAGE/obs-moq/"
145+
fi
146+
147+
cp "$SCRIPT_DIR/LICENSE" "$STAGE/"
148+
cp "$SCRIPT_DIR/README.md" "$STAGE/"
149+
150+
# Archive with CMake's tar so we don't depend on zip/gtar being present
151+
# (notably on the Windows runner). tar.gz on unix, zip on macOS/Windows.
152+
(
153+
cd "$OUTPUT_DIR"
154+
if [[ "$KIND" == "unix" ]]; then
155+
ARCHIVE="$NAME.tar.gz"
156+
cmake -E tar czf "$ARCHIVE" "$NAME"
157+
else
158+
ARCHIVE="$NAME.zip"
159+
cmake -E tar cf "$ARCHIVE" --format=zip "$NAME"
160+
fi
161+
rm -rf "$NAME"
162+
echo "Created: $OUTPUT_DIR/$ARCHIVE"
163+
)

0 commit comments

Comments
 (0)