- Refactor the Go proxy for dependency injection: every proxy server, the bootstrap, the signal handler, the load balancers, and AMF0 now accept functional-option seams (factories/closures) so tests can inject fakes without binding real sockets, talking to real Redis, or racing on package globals. - Drop the package-global `lb.SrsLoadBalancer`. The bootstrap creates the LB locally and threads it through every proxy server constructor. Two old global indirections in `internal/signal` and `internal/rtmp/amf0` are likewise replaced by per-instance fields. - Rename `internal/server` → `internal/proxy` and rename the `lb` public surface for clarity: `SRSLoadBalancer` is split into `OriginService` / `HLSService` / `RTCService` and recomposed as `OriginLoadBalancer`; `SRSServer` → `OriginServer`; all proxy server types gain a `Proxy` qualifier (e.g. `RTMPServer` → `RTMPProxyServer`). - Extract the Redis client behind a new `internal/redisclient` package with a minimal `RedisClient` interface and a counterfeiter fake. - Add counterfeiter fakes (`proxyfakes`, `lbfakes`, `redisclientfakes`) and ~7.5k lines of unit tests covering bootstrap, memory + Redis LBs, all five proxy servers, the signal handler, and AMF0. - Add two new E2E flows — `proxy-e2e-srt-test.sh` (SRT publish through proxy, verify SRT/RTMP/HTTP-FLV/HLS playback) and `proxy-e2e-whip-test.sh` (WHIP publish, verify RTMP/HTTP-FLV/HLS via origin `rtc_to_rtmp`) — plus `setup-ffmpeg-with-whip.sh`, a macOS builder for an ffmpeg with openssl-DTLS WHIP and SRT support that the two scripts auto-invoke when needed. - Workspace reorg: move `memory/` and `skills/` to the repo root so all agent tools (Claude / Codex / Kiro / OpenClaw) share one source of truth via symlinks. Sync `docs/proxy/proxy-load-balancer.md` and `memory/srs-codebase-map.md` with the new names. No protocol, log, HTTP API, or wire-format changes. Refactor only — all externally observable proxy behavior is unchanged. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
233 lines
7.5 KiB
Bash
Executable File
233 lines
7.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Build ffmpeg from source with the codecs and protocols the SRS proxy E2E
|
|
# tests need — in particular WHIP (WebRTC-HTTP Ingestion Protocol), which the
|
|
# default Homebrew formulas (vanilla + homebrew-ffmpeg tap) do not enable.
|
|
#
|
|
# Modelled on the ossrs/dev-docker ubuntu20 base images:
|
|
# https://github.com/ossrs/dev-docker/blob/ubuntu20/Dockerfile.base
|
|
# https://github.com/ossrs/dev-docker/blob/ubuntu20/Dockerfile.base2
|
|
# https://github.com/ossrs/dev-docker/blob/ubuntu20/Dockerfile.base3
|
|
# The Dockerfiles describe *which* libraries and configure flags to enable;
|
|
# on macOS we install those deps via Homebrew (shared libs) instead of
|
|
# rebuilding each one from tarballs. ffmpeg itself is built from source so we
|
|
# can pass --enable-libsrtp / --enable-openssl, which neither Homebrew formula
|
|
# turns on.
|
|
#
|
|
# Output:
|
|
# ~/.local/src/ffmpeg (git clone)
|
|
# ~/.local/bin/{ffmpeg,ffprobe,ffplay}
|
|
# ~/.local/lib, ~/.local/share, ... (ffmpeg --prefix tree)
|
|
#
|
|
# Re-running is safe: deps already installed are skipped, the git clone is
|
|
# reused (fetch + checkout), and ffmpeg is rebuilt incrementally.
|
|
set -e
|
|
|
|
FFMPEG_TAG="${FFMPEG_TAG:-n8.1.1}"
|
|
PREFIX="${PREFIX:-$HOME/.local}"
|
|
SRC_DIR="${SRC_DIR:-$HOME/.local/src/ffmpeg}"
|
|
JOBS="${JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
|
|
|
|
REQUIRED_BREW_PKGS=(
|
|
# build toolchain (Homebrew renamed pkg-config → pkgconf in 2024)
|
|
pkgconf nasm
|
|
# WHIP needs DTLS (openssl). ffmpeg's WHIP muxer uses ffmpeg's *internal*
|
|
# SRTP implementation (srtp_protocol_select="rtp_protocol srtp" in configure)
|
|
# — no external libsrtp dependency, so no extra brew package here.
|
|
openssl@3
|
|
# SRT
|
|
srt
|
|
# video codecs (matches Dockerfile.base / .base2)
|
|
x264 x265 libvpx
|
|
# audio codecs (fdk-aac requires --enable-nonfree below)
|
|
fdk-aac lame opus
|
|
# subtitle / font stack (matches Dockerfile.base3)
|
|
freetype fontconfig harfbuzz fribidi libass
|
|
)
|
|
|
|
log() { printf '\n=== %s ===\n' "$*"; }
|
|
|
|
# --- Pre-checks ---------------------------------------------------------------
|
|
|
|
if [[ "$(uname -s)" != "Darwin" ]]; then
|
|
echo "Error: this script targets macOS. For Linux, follow Dockerfile.base/2/3 directly." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$(id -u)" -eq 0 ]]; then
|
|
echo "Error: do not run as root. Homebrew refuses, and the prefix is your \$HOME." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v brew &>/dev/null; then
|
|
echo "Error: Homebrew not found. Install from https://brew.sh and retry." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v git &>/dev/null; then
|
|
echo "Error: git not found in PATH." >&2
|
|
exit 1
|
|
fi
|
|
|
|
log "Configuration"
|
|
echo "FFmpeg tag : $FFMPEG_TAG"
|
|
echo "Prefix : $PREFIX"
|
|
echo "Source dir : $SRC_DIR"
|
|
echo "Parallel : $JOBS jobs"
|
|
|
|
# --- Step 1: Install Homebrew deps -------------------------------------------
|
|
|
|
log "Step 1: Installing Homebrew dependencies"
|
|
INSTALLED="$(brew list --formula 2>/dev/null || true)"
|
|
TO_INSTALL=()
|
|
for pkg in "${REQUIRED_BREW_PKGS[@]}"; do
|
|
if echo "$INSTALLED" | grep -qx "$pkg"; then
|
|
echo " ok $pkg"
|
|
else
|
|
echo " miss $pkg"
|
|
TO_INSTALL+=("$pkg")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#TO_INSTALL[@]} -gt 0 ]]; then
|
|
echo ""
|
|
echo "Installing missing packages: ${TO_INSTALL[*]}"
|
|
brew install "${TO_INSTALL[@]}"
|
|
else
|
|
echo ""
|
|
echo "All required Homebrew packages already installed."
|
|
fi
|
|
|
|
# --- Step 2: Clone or refresh ffmpeg source ----------------------------------
|
|
|
|
log "Step 2: Fetching ffmpeg source at $FFMPEG_TAG"
|
|
mkdir -p "$(dirname "$SRC_DIR")"
|
|
if [[ ! -d "$SRC_DIR/.git" ]]; then
|
|
# Shallow clone of just the tag we want — full history is ~600 MB and
|
|
# takes minutes to index; this drops to ~80 MB and seconds.
|
|
git clone --depth 1 --branch "$FFMPEG_TAG" \
|
|
https://github.com/FFmpeg/FFmpeg.git "$SRC_DIR"
|
|
else
|
|
cd "$SRC_DIR"
|
|
git fetch --depth 1 --tags --quiet origin "$FFMPEG_TAG"
|
|
fi
|
|
cd "$SRC_DIR"
|
|
git checkout --quiet "$FFMPEG_TAG"
|
|
echo "Checked out: $(git describe --tags --always)"
|
|
|
|
# --- Step 3: Configure --------------------------------------------------------
|
|
|
|
log "Step 3: Configuring ffmpeg"
|
|
|
|
# openssl@3 is keg-only in Homebrew, so its .pc files are not on the default
|
|
# PKG_CONFIG_PATH. Other deps (lame in particular) ship no .pc at all, so
|
|
# also pass --extra-cflags / --extra-ldflags pointing at the Homebrew prefix
|
|
# (works for both Apple Silicon /opt/homebrew and Intel /usr/local).
|
|
BREW_PREFIX="$(brew --prefix)"
|
|
OPENSSL_PREFIX="$(brew --prefix openssl@3)"
|
|
export PKG_CONFIG_PATH="$OPENSSL_PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
|
|
echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH"
|
|
|
|
# --enable-nonfree : required by --enable-libfdk-aac
|
|
# --enable-gpl : required by --enable-libx264 / --enable-libx265 / --enable-libass
|
|
# --enable-version3 : allow (L)GPLv3 components alongside GPL/nonfree
|
|
# --enable-openssl : DTLS backend; the WHIP muxer needs this for the
|
|
# DTLS-SRTP handshake. ffmpeg's WHIP muxer uses ffmpeg's
|
|
# *internal* SRTP (libavformat/srtp.c), not external
|
|
# libsrtp — so no --enable-libsrtp flag exists/is needed.
|
|
# --enable-libsrt : SRT protocol (publish/play)
|
|
./configure \
|
|
--prefix="$PREFIX" \
|
|
--extra-cflags="-I$BREW_PREFIX/include" \
|
|
--extra-ldflags="-L$BREW_PREFIX/lib" \
|
|
--enable-gpl \
|
|
--enable-nonfree \
|
|
--enable-version3 \
|
|
--enable-openssl \
|
|
--enable-libsrt \
|
|
--enable-libx264 \
|
|
--enable-libx265 \
|
|
--enable-libvpx \
|
|
--enable-libfdk-aac \
|
|
--enable-libmp3lame \
|
|
--enable-libopus \
|
|
--enable-libass \
|
|
--enable-libfreetype \
|
|
--enable-libfontconfig \
|
|
--enable-libharfbuzz \
|
|
--enable-libfribidi \
|
|
--enable-videotoolbox \
|
|
--enable-audiotoolbox \
|
|
--disable-debug
|
|
|
|
# --- Step 4: Build and install -----------------------------------------------
|
|
|
|
log "Step 4: Building ffmpeg ($JOBS jobs)"
|
|
make -j"$JOBS"
|
|
|
|
log "Step 5: Installing to $PREFIX"
|
|
make install
|
|
|
|
# --- Step 5: Verify -----------------------------------------------------------
|
|
|
|
log "Step 6: Verifying installed binary"
|
|
FFMPEG_BIN="$PREFIX/bin/ffmpeg"
|
|
if [[ ! -x "$FFMPEG_BIN" ]]; then
|
|
echo "Error: $FFMPEG_BIN missing after install." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
"$FFMPEG_BIN" -version | head -2
|
|
echo ""
|
|
|
|
if "$FFMPEG_BIN" -hide_banner -muxers 2>/dev/null | grep -qw whip; then
|
|
echo "PASS: WHIP muxer is available."
|
|
else
|
|
echo "FAIL: WHIP muxer is NOT in the installed ffmpeg." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if "$FFMPEG_BIN" -hide_banner -protocols 2>/dev/null | grep -qw srt; then
|
|
echo "PASS: SRT protocol is available."
|
|
else
|
|
echo "FAIL: SRT protocol is NOT in the installed ffmpeg." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if "$FFMPEG_BIN" -hide_banner -codecs 2>/dev/null | grep -E "libx264|libx265" | grep -q libx264; then
|
|
echo "PASS: libx264 encoder is available."
|
|
else
|
|
echo "FAIL: libx264 encoder missing." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if "$FFMPEG_BIN" -hide_banner -codecs 2>/dev/null | grep -q libx265; then
|
|
echo "PASS: libx265 encoder is available."
|
|
else
|
|
echo "FAIL: libx265 encoder missing." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
log "Done"
|
|
echo "Binary: $FFMPEG_BIN"
|
|
echo "Version: $("$FFMPEG_BIN" -version | head -1)"
|
|
echo ""
|
|
|
|
# Warn if PATH won't pick up the new binary.
|
|
if ! echo ":$PATH:" | grep -q ":$PREFIX/bin:"; then
|
|
echo "NOTE: $PREFIX/bin is not on your PATH."
|
|
echo " Add this to ~/.zshrc (or your shell rc):"
|
|
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
|
|
echo " Then 'which ffmpeg' should resolve to $FFMPEG_BIN."
|
|
else
|
|
RESOLVED="$(command -v ffmpeg || true)"
|
|
if [[ "$RESOLVED" == "$FFMPEG_BIN" ]]; then
|
|
echo "PATH check: 'ffmpeg' resolves to $RESOLVED ✓"
|
|
else
|
|
echo "NOTE: $PREFIX/bin is on PATH but 'ffmpeg' currently resolves to:"
|
|
echo " $RESOLVED"
|
|
echo " Reorder PATH so $PREFIX/bin comes before $(dirname "$RESOLVED")."
|
|
fi
|
|
fi
|