mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
test(sandbox): add ffmpeg-based non-Mux local live HLS source
local-live.sh generates a sliding-window demuxed CMAF/fMP4 live stream with ffmpeg's HLS muxer (master + EXT-X-MEDIA audio group + fMP4 init/segments) to a temp dir, served with CORS and no caching — a no-creds local live source for the SPF engine and the hls.js/native sandbox pages. Documents the no-cache requirement (a positive max-age on a live playlist freezes the client's window — a server misconfiguration, not a player bug). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
434390916f
commit
553061a30b
@@ -19,7 +19,26 @@ One broadcast yields **two** sources to test:
|
||||
Both are CMAF/fMP4, which is what the SPF MSE pipeline appends (it does not
|
||||
transmux MPEG-TS, so generic TS live test streams won't play there).
|
||||
|
||||
## Prerequisites
|
||||
## Local source (no Mux, no creds)
|
||||
|
||||
For a sliding-window live source without a Mux account, `local-live.sh` uses
|
||||
ffmpeg's own HLS muxer to write **demuxed CMAF/fMP4** (master + audio group +
|
||||
fMP4 init/segments) to a temp dir, served with CORS and **no caching**:
|
||||
|
||||
```sh
|
||||
./local-live.sh # serves http://localhost:8080/master.m3u8
|
||||
# then open the sandbox at that src (start `pnpm dev` first):
|
||||
open "http://localhost:5173/spf-segment-loading/?src=http://localhost:8080/master.m3u8&muted=true&autoplay=true&preload=auto"
|
||||
```
|
||||
|
||||
The no-cache part is load-bearing (`http-server -c-1`): a live playlist served
|
||||
with a positive `max-age` makes reloads return a stale media-sequence, so the
|
||||
client's window never advances and playback stalls — a server misconfiguration,
|
||||
not a player bug. Mux gets this right; a plain static file server does not by
|
||||
default. (This is VOD-free and doesn't model DVR/EVENT — use the Mux flow above
|
||||
for those.)
|
||||
|
||||
## Prerequisites (Mux flow)
|
||||
|
||||
- `ffmpeg`, `curl`, `jq` on `PATH`.
|
||||
- A Mux access token (Mux dashboard → Settings → Access Tokens). Provide it via
|
||||
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Non-Mux local live HLS source — no Mux account or creds. ffmpeg generates a
|
||||
# test pattern + tone, and its HLS muxer writes demuxed CMAF/fMP4 (master +
|
||||
# audio group + fMP4 init/segments) to a temp dir, served with CORS and NO
|
||||
# caching. SPF plays it (also the hls.js / native sandbox pages).
|
||||
#
|
||||
# A live playlist MUST be served no-cache (http-server -c-1): otherwise reloads
|
||||
# return a stale media-sequence and the client's window never advances.
|
||||
#
|
||||
# Usage: ./local-live.sh [--port N] (default 8080). Ctrl-C to stop.
|
||||
# Then open, e.g.:
|
||||
# http://localhost:5173/spf-segment-loading/?src=http://localhost:8080/master.m3u8&muted=true&autoplay=true&preload=auto
|
||||
#
|
||||
# Requires: ffmpeg, npx (http-server).
|
||||
set -uo pipefail
|
||||
|
||||
PORT=8080
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--port) PORT="$2"; shift 2 ;;
|
||||
-h|--help) sed -n '2,13p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
*) echo "unknown argument: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
OUT="${TMPDIR:-/tmp}/spf-local-live"
|
||||
rm -rf "$OUT"; mkdir -p "$OUT"
|
||||
|
||||
SVR=""; FF=""
|
||||
cleanup() { kill "$SVR" "$FF" 2>/dev/null; exit 0; }
|
||||
trap cleanup INT TERM
|
||||
|
||||
npx http-server "$OUT" -p "$PORT" --cors -c-1 -s & SVR=$!
|
||||
sleep 1
|
||||
|
||||
echo "▶ live: http://localhost:$PORT/master.m3u8 (sliding-window, demuxed CMAF)"
|
||||
echo " sandbox: http://localhost:5173/spf-segment-loading/?src=http://localhost:$PORT/master.m3u8&muted=true&autoplay=true&preload=auto"
|
||||
echo " Ctrl-C to stop."
|
||||
|
||||
# Demuxed via --var_stream_map (video variant + EXT-X-MEDIA audio group); fMP4
|
||||
# segments via -hls_segment_type fmp4; PDT for cross-track/reload timeline sync.
|
||||
ffmpeg -re \
|
||||
-f lavfi -i testsrc=size=1280x720:rate=30 \
|
||||
-f lavfi -i sine=frequency=440:sample_rate=48000 \
|
||||
-map 0:v -map 1:a \
|
||||
-c:v libx264 -preset veryfast -tune zerolatency -g 60 -keyint_min 60 -pix_fmt yuv420p \
|
||||
-c:a aac -b:a 128k -ar 48000 \
|
||||
-f hls -hls_time 2 -hls_list_size 6 \
|
||||
-hls_flags independent_segments+omit_endlist+program_date_time \
|
||||
-hls_segment_type fmp4 \
|
||||
-hls_fmp4_init_filename 'init_%v.mp4' \
|
||||
-hls_segment_filename "$OUT/seg_%v_%05d.m4s" \
|
||||
-master_pl_name master.m3u8 \
|
||||
-var_stream_map "v:0,agroup:aud,name:video a:0,agroup:aud,default:yes,name:audio" \
|
||||
"$OUT/stream_%v.m3u8" > "$OUT/ffmpeg.log" 2>&1 & FF=$!
|
||||
wait "$FF"
|
||||
Reference in New Issue
Block a user