srs/skills/srs-develop/scripts/setup-ffmpeg-with-whip.sh
Winlin 6ee6f1ca5f Proxy: Refactor for testability; add SRT/WHIP E2E and unit tests. v7.0.148 (#4675)
- 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>
2026-05-17 12:09:07 -04:00

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