Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 4 additions & 14 deletions demo/boy/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@ default:
"just wait && just web"

# Download the given ROM from our R2 bucket.
[positional-arguments]
download rom:
#!/usr/bin/env bash
if [ ! -f "rom/{{ rom }}" ]; then
mkdir -p rom
curl -fSL -o "rom/{{ rom }}.tmp" "https://rom.moq.dev/{{ rom }}"
mv "rom/{{ rom }}.tmp" "rom/{{ rom }}"
fi
@../../sh/demo/download.sh "https://rom.moq.dev/$1" "rom/$1"

# Big2Small (puzzle): https://mdsteele.itch.io/big2small
b2s:
Expand Down Expand Up @@ -70,15 +66,9 @@ upload file:
bun wrangler r2 object put "rom-moq-dev/{{ file }}" --file "rom/{{ file }}" --remote

# Sync all ROMs to R2
[positional-arguments]
sync dir="rom":
#!/usr/bin/env bash
set -euo pipefail
for file in "{{ dir }}"/*; do
[ -e "$file" ] || continue
key=$(basename "$file")
echo "Uploading $key..."
bun wrangler r2 object put "rom-moq-dev/$key" --file "$file" --remote
done
../../sh/demo/sync.sh rom-moq-dev "$@"

# List files in the R2 bucket
list:
Expand Down
51 changes: 5 additions & 46 deletions demo/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,17 @@ set fallback

# Run the web demo (default).
#
# Picks the first free port at/after 4443 (both UDP for QUIC and TCP for HTTP)
# so multiple worktrees can run `just dev` without colliding. The relay reads

# the port from MOQ_LISTEN/MOQ_WEB_HTTP_LISTEN, overriding localhost.toml.
# Picks the first free port at/after 4443 so multiple worktrees can run
# `just dev` without colliding; see sh/demo/run.sh.
default:
#!/usr/bin/env bash
set -euo pipefail
bun install

port=$(just port)
if [ "$port" != "4443" ]; then
echo ">>> Port 4443 is busy, using $port instead" >&2
fi

base="http://localhost:$port"

# Scope the bind env vars to the relay only. The unified `moq` binary also
# reads MOQ_LISTEN, so exporting it globally would make the `pub` client
# try to start a server (and fail without a TLS cert).
bun run concurrently --kill-others --names rly,bbb,web --prefix-colors auto \
"MOQ_LISTEN='[::]:$port' MOQ_WEB_HTTP_LISTEN='[::]:$port' just relay" \
"just wait $base/certificate.sha256 && just pub bbb $base" \
"just wait $base/certificate.sha256 && just web serve $base"

# Find the first free port at/after `start` (checked on both TCP and UDP).
# `lsof` isn't available everywhere (e.g. Git Bash on Windows); without it we
# can't probe, so fall back to `start` and let the relay surface a bind error if

# it's actually taken.
[private]
port start="4443":
#!/usr/bin/env bash
set -euo pipefail
port={{ start }}
if command -v lsof >/dev/null 2>&1; then
while lsof -nP -iTCP:"$port" -sTCP:LISTEN -t >/dev/null 2>&1 \
|| lsof -nP -iUDP:"$port" -t >/dev/null 2>&1; do
port=$((port + 1))
done
fi
echo "$port"
../sh/demo/run.sh

# Wait for the localhost relay to be ready by repeatedly requesting the cert
[positional-arguments]
[private]
wait url="http://localhost:4443/certificate.sha256":
#!/usr/bin/env bash
set -euo pipefail
for i in $(seq 1 600); do
curl -sf "{{ url }}" > /dev/null 2>&1 && exit 0
sleep 1
done
exit 1
../sh/demo/wait.sh "$@"

# Throttle UDP traffic for testing (macOS only, requires sudo).
throttle:
Expand Down
171 changes: 32 additions & 139 deletions demo/pub/justfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
set fallback
set shell := ["bash", "-euo", "pipefail", "-c"]

# --- R2 bucket management (vid.moq.dev) ---

Expand All @@ -18,15 +19,9 @@ upload file:
bun wrangler r2 object put "video-moq-dev/{{ file }}" --file "media/{{ file }}" --remote

# Sync all files in a local directory to R2
[positional-arguments]
sync dir="media":
#!/usr/bin/env bash
set -euo pipefail
for file in "{{ dir }}"/*; do
[ -e "$file" ] || continue
key=$(basename "$file")
echo "Uploading $key..."
bun wrangler r2 object put "video-moq-dev/$key" --file "$file" --remote
done
../../sh/demo/sync.sh video-moq-dev "$@"

# List files in the bucket
list:
Expand All @@ -36,6 +31,7 @@ list:
# --- Publishing ---

# Publish Big Buck Bunny to a relay server.
[positional-arguments]
bbb url='http://localhost:4443' *args:
just download bbb
just download bbb-sd
Expand All @@ -44,7 +40,7 @@ bbb url='http://localhost:4443' *args:
-stream_loop -1 -re -i media/bbb.mp4 \
-stream_loop -1 -re -i media/bbb-sd.mp4 \
-map 0 -map 1:v -c copy -f mpegts -pes_payload_size 0 - \
| cargo run --bin moq -- {{ args }} --connect "{{ url }}" --broadcast bbb.hang import ts
| cargo run --bin moq -- "${@:2}" --connect "{{ url }}" --broadcast bbb.hang import ts

# Encode the video-only SD rendition, preserving source timestamps and loop duration.
encode-bbb-sd:
Expand All @@ -58,19 +54,22 @@ check-bbb:
python3 bbb.py check

# Publish Tears of Steel to a relay server.
[positional-arguments]
tos url='http://localhost:4443' *args:
just download tos
just ts tos "{{ url }}" {{ args }}
just ts tos "$@"

# Publish a video using ffmpeg (CMAF / fMP4) to a relay server.
[positional-arguments]
cmaf name url='http://localhost:4443' *args:
cargo build --bin moq
just ffmpeg-cmaf "media/{{ name }}.mp4" | cargo run --bin moq -- {{ args }} --connect "{{ url }}" --broadcast "{{ name }}.hang" import fmp4
just ffmpeg-cmaf "media/{{ name }}.mp4" | cargo run --bin moq -- "${@:3}" --connect "{{ url }}" --broadcast "{{ name }}.hang" import fmp4

# Publish a video using ffmpeg (MPEG-TS) to a relay server.
[positional-arguments]
ts name url='http://localhost:4443' *args:
cargo build --bin moq
just ffmpeg-ts "media/{{ name }}.mp4" | cargo run --bin moq -- {{ args }} --connect "{{ url }}" --broadcast "{{ name }}.hang" import ts
just ffmpeg-ts "media/{{ name }}.mp4" | cargo run --bin moq -- "${@:3}" --connect "{{ url }}" --broadcast "{{ name }}.hang" import ts

# Publish a video via iroh.
iroh name url prefix="":
Expand All @@ -79,82 +78,13 @@ iroh name url prefix="":
just ffmpeg-ts "media/{{ name }}.mp4" | cargo run --bin moq -- --iroh-enabled --connect "{{ url }}" --broadcast "{{ prefix }}{{ name }}.hang" import ts

# Generate and ingest an HLS stream from a video file.
[positional-arguments]
hls name relay="http://localhost:4443":
#!/usr/bin/env bash
set -euo pipefail

just download "{{ name }}"

INPUT="media/{{ name }}.mp4"
OUT_DIR="media/{{ name }}"

rm -rf "$OUT_DIR"
mkdir -p "$OUT_DIR"

echo ">>> Generating HLS stream to disk (1280x720 + 256x144)..."

ffmpeg -hide_banner -loglevel warning -re -stream_loop -1 -i "$INPUT" \
-filter_complex "\
[0:v]split=2[v0][v1]; \
[v0]scale=-2:720[v720]; \
[v1]scale=-2:144[v144]" \
-map "[v720]" -map "[v144]" -map 0:a:0 \
-r 25 -preset veryfast -g 50 -keyint_min 50 -sc_threshold 0 \
-c:v:0 libx264 -profile:v:0 high -level:v:0 4.1 -pix_fmt:v:0 yuv420p -tag:v:0 avc1 \
-b:v:0 4M -maxrate:v:0 4.4M -bufsize:v:0 8M \
-c:v:1 libx264 -profile:v:1 high -level:v:1 4.1 -pix_fmt:v:1 yuv420p -tag:v:1 avc1 \
-b:v:1 300k -maxrate:v:1 330k -bufsize:v:1 600k \
-c:a aac -b:a 128k \
-f hls -hls_time 2 -hls_list_size 6 \
-hls_flags independent_segments+delete_segments \
-hls_segment_type fmp4 \
-master_pl_name master.m3u8 \
-var_stream_map "v:0,agroup:audio,name:720 v:1,agroup:audio,name:144 a:0,agroup:audio,name:audio" \
-hls_segment_filename "$OUT_DIR/v%v/segment_%09d.m4s" \
"$OUT_DIR/v%v/stream.m3u8" &

FFMPEG_PID=$!

echo ">>> Waiting for HLS playlist generation..."
for i in {1..30}; do
if [ -f "$OUT_DIR/master.m3u8" ]; then break; fi
sleep 0.5
done

if [ ! -f "$OUT_DIR/master.m3u8" ]; then
kill $FFMPEG_PID 2>/dev/null || true
echo "Error: master.m3u8 not generated in time"
exit 1
fi

echo ">>> Waiting for variant playlists..."
sleep 2
for i in {1..20}; do
if [ -f "$OUT_DIR/v0/stream.m3u8" ] || [ -f "$OUT_DIR/v720/stream.m3u8" ] || [ -f "$OUT_DIR/v144/stream.m3u8" ] || [ -f "$OUT_DIR/vaudio/stream.m3u8" ]; then
break
fi
sleep 0.5
done

CLEANUP_CALLED=false
cleanup() {
if [ "$CLEANUP_CALLED" = "true" ]; then return; fi
CLEANUP_CALLED=true
echo "Shutting down..."
kill $FFMPEG_PID 2>/dev/null || true
sleep 0.5
kill -9 $FFMPEG_PID 2>/dev/null || true
}
trap cleanup SIGINT SIGTERM EXIT

echo ">>> Importing HLS via moq-cli"
cargo run --bin moq -- --connect "{{ relay }}" --broadcast "{{ name }}.hang" import hls "$OUT_DIR/master.m3u8"
EXIT_CODE=$?

cleanup
exit $EXIT_CODE
just download "$1"
../../sh/demo/hls.sh "$@"

# Publish a video using H.264 Annex B format.
[positional-arguments]
h264 name url="http://localhost:4443" *args:
just download "{{ name }}"
cargo build --bin moq
Expand All @@ -165,68 +95,31 @@ h264 name url="http://localhost:4443" *args:
-c:v copy -an \
-bsf:v h264_mp4toannexb \
-f h264 \
- | cargo run --bin moq -- {{ args }} --connect "{{ url }}" --broadcast "{{ name }}.hang" import avc3
- | cargo run --bin moq -- "${@:3}" --connect "{{ url }}" --broadcast "{{ name }}.hang" import avc3

# Publish a video using ffmpeg directly from moq to the localhost.
[positional-arguments]
serve name *args:
just download "{{ name }}"
cargo build --bin moq
just ffmpeg-ts "media/{{ name }}.mp4" | cargo run --bin moq -- {{ args }} --listen "[::]:4443" --listen-tls-generate "localhost" --auth-public "**" --broadcast "{{ name }}.hang" import ts
just ffmpeg-ts "media/{{ name }}.mp4" | cargo run --bin moq -- "${@:2}" --listen "[::]:4443" --listen-tls-generate "localhost" --auth-public "**" --broadcast "{{ name }}.hang" import ts

# Generate and serve an HLS stream from a video for testing.
[positional-arguments]
serve-hls name port="8000":
#!/usr/bin/env bash
set -euo pipefail

just download "{{ name }}"

INPUT="media/{{ name }}.mp4"
OUT_DIR="media/{{ name }}"

rm -rf "$OUT_DIR"
mkdir -p "$OUT_DIR"

echo ">>> Starting HLS stream generation..."
echo ">>> Master playlist: http://localhost:{{ port }}/master.m3u8"

cleanup() {
echo "Shutting down..."
kill $(jobs -p) 2>/dev/null || true
exit 0
}
trap cleanup SIGINT SIGTERM

ffmpeg -loglevel warning -re -stream_loop -1 -i "$INPUT" \
-map 0:v:0 -map 0:v:0 -map 0:a:0 \
-r 25 -preset veryfast -g 50 -keyint_min 50 -sc_threshold 0 \
-c:v:0 libx264 -profile:v:0 high -level:v:0 4.1 -pix_fmt:v:0 yuv420p -tag:v:0 avc1 -bsf:v:0 dump_extra -b:v:0 4M -vf:0 "scale=1920:-2" \
-c:v:1 libx264 -profile:v:1 high -level:v:1 4.1 -pix_fmt:v:1 yuv420p -tag:v:1 avc1 -bsf:v:1 dump_extra -b:v:1 300k -vf:1 "scale=256:-2" \
-c:a aac -b:a 128k \
-f hls \
-hls_time 2 -hls_list_size 12 \
-hls_flags independent_segments+delete_segments \
-hls_segment_type fmp4 \
-master_pl_name master.m3u8 \
-var_stream_map "v:0,agroup:audio v:1,agroup:audio a:0,agroup:audio" \
-hls_segment_filename "$OUT_DIR/v%v/segment_%09d.m4s" \
"$OUT_DIR/v%v/stream.m3u8" &

sleep 2
echo ">>> HTTP server: http://localhost:{{ port }}/"
cd "$OUT_DIR" && python3 -m http.server {{ port }}
just download "$1"
../../sh/demo/serve-hls.sh "$@"

# Publish the clock broadcast.
[arg("action", pattern="publish|subscribe")]
[positional-arguments]
clock action url="http://localhost:4443" *args:
@if [ "{{ action }}" != "publish" ] && [ "{{ action }}" != "subscribe" ]; then \
echo "Error: action must be 'publish' or 'subscribe', got '{{ action }}'" >&2; \
exit 1; \
fi

cargo run -p moq-tokio --example clock -- --connect "{{ url }}" --broadcast "clock" {{ args }} {{ action }}
cargo run -p moq-tokio --example clock -- --connect "{{ url }}" --broadcast "clock" "${@:3}" "{{ action }}"

# --- GStreamer ---

# Publish a video using GStreamer to a relay server.
[positional-arguments]
gst name url='http://localhost:4443' *args:
just download "{{ name }}"
cargo build -p moq-gst
Expand All @@ -235,34 +128,34 @@ gst name url='http://localhost:4443' *args:
gst-launch-1.0 -v -e multifilesrc location="media/{{ name }}.mp4" loop=true ! parsebin name=parse \
parse. ! queue ! identity sync=true ! mux.sink_0 \
parse. ! queue ! identity sync=true ! mux.sink_1 \
moqsink name=mux url="{{ url }}" broadcast="{{ name }}.hang" {{ args }}
moqsink name=mux url="{{ url }}" broadcast="{{ name }}.hang" "${@:3}"

# --- Private helpers ---

# Download a test video (already fragmented on vid.moq.dev).
[positional-arguments]
[private]
download name:
@if [ ! -f "media/{{ name }}.mp4" ]; then \
mkdir -p media; \
curl -fsSL "https://vid.moq.dev/{{ name }}.mp4" -o "media/{{ name }}.mp4"; \
fi
@../../sh/demo/download.sh "https://vid.moq.dev/$1.mp4" "media/$1.mp4"

# Convert an h264 input file to CMAF (fmp4) format to stdout, with one fragment per sample.
# -frag_duration rather than frag_every_frame: the latter repeats a fragment's tfdt when it
# interleaves audio and video, landing two samples on one timestamp.
[positional-arguments]
[private]
ffmpeg-cmaf input output='-' *args:
ffmpeg -hide_banner -v quiet \
-stream_loop -1 -re \
-i "{{ input }}" \
-c copy \
-f mp4 -movflags cmaf+separate_moof+delay_moov+skip_trailer -frag_duration 1000 {{ args }} {{ output }}
-f mp4 -movflags cmaf+separate_moof+delay_moov+skip_trailer -frag_duration 1000 "${@:3}" "{{ output }}"

# Convert an h264 input file to MPEG-TS format to stdout, with one PES per audio frame.
[positional-arguments]
[private]
ffmpeg-ts input output='-' *args:
ffmpeg -hide_banner -v quiet \
-stream_loop -1 -re \
-i "{{ input }}" \
-c copy \
-f mpegts -pes_payload_size 0 {{ args }} {{ output }}
-f mpegts -pes_payload_size 0 "${@:3}" "{{ output }}"
Loading
Loading