- New scripts/provision-stream-droplet.sh (modeled on gpu one) for cheap DO droplet with docker + v4l2loopback + mediamtx ready. - scripts/hotel-srt-push.sh: macOS ffmpeg one-liner helper for reliable modest-bitrate SRT contribution over bad WiFi. - New @features/broadcast/controller/: self-contained Bun server with embedded Tailwind chat UI, full xAI Grok-4.3 tool-calling agent, pure-WebSocket obs-websocket v5 client, dynamic destination + ffmpeg fanout manager. - Full docs + RUNBOOK with architecture, provisioning, hotel push, OBS notes, security, troubleshooting, and verification notes. - Matches the 'RTMP relay for simulcast' vision already promised in performer marketing. The hotel side sends ~3 Mbps SRT. All compute, final encode, and broadcast bitrate (to Twitch/YouTube/etc.) happens on the DO network. Control is natural language chat to the LLM.
95 lines
3 KiB
Bash
Executable file
95 lines
3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Hotel / weak-WiFi SRT contribution push to a quinn.cast relay droplet.
|
|
#
|
|
# This is the "just a video stream from my hotel wifi" side.
|
|
# Everything heavy (final encode, bitrate, multi-platform fanout, OBS production) happens on DO.
|
|
#
|
|
# Usage:
|
|
# ./scripts/hotel-srt-push.sh --help
|
|
# ./scripts/hotel-srt-push.sh --target 203.0.113.77:8890 --streamid publish:live --bitrate 3500
|
|
#
|
|
# macOS only (avfoundation). Linux/Windows users: adapt the -f and -i lines.
|
|
#
|
|
# Tips for terrible hotel WiFi:
|
|
# - Keep bitrate modest (2500-5000k). Let the droplet do the heavy lifting.
|
|
# - Use a wired connection to the hotel router if possible.
|
|
# - Close everything else using upload.
|
|
# - The SRT protocol + mediamtx gives you packet recovery that plain RTMP does not.
|
|
|
|
set -euo pipefail
|
|
|
|
TARGET=""
|
|
STREAMID="publish:live"
|
|
BITRATE_K=3500
|
|
FRAMERATE=30
|
|
RES="1280x720"
|
|
AUDIO_DEV=":0" # default; list devices with ffmpeg -f avfoundation -list_devices true -i ""
|
|
VIDEO_DEV="0" # usually the first camera
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--target) TARGET="$2"; shift 2 ;;
|
|
--streamid) STREAMID="$2"; shift 2 ;;
|
|
--bitrate) BITRATE_K="$2"; shift 2 ;;
|
|
--res) RES="$2"; shift 2 ;;
|
|
--framerate) FRAMERATE="$2"; shift 2 ;;
|
|
--video) VIDEO_DEV="$2"; shift 2 ;;
|
|
--audio) AUDIO_DEV="$2"; shift 2 ;;
|
|
--list-devices)
|
|
ffmpeg -f avfoundation -list_devices true -i "" || true
|
|
exit 0
|
|
;;
|
|
-h|--help)
|
|
cat <<EOF
|
|
Hotel SRT push helper
|
|
|
|
Required:
|
|
--target IP:PORT e.g. 203.0.113.77:8890 (the droplet's public SRT listener)
|
|
|
|
Optional:
|
|
--streamid STR default: publish:live
|
|
--bitrate K default 3500 (keep low on hotel WiFi)
|
|
--res WxH default 1280x720
|
|
--framerate N default 30
|
|
--video INDEX avfoundation video device (default 0)
|
|
--audio INDEX avfoundation audio device (default :0 for default mic)
|
|
--list-devices show available cameras/mics then exit
|
|
|
|
Example (most common):
|
|
./scripts/hotel-srt-push.sh --target 203.0.113.77:8890 --bitrate 3000
|
|
|
|
Once running, open the LLM controller on the droplet and say "start broadcast".
|
|
EOF
|
|
exit 0
|
|
;;
|
|
*) echo "Unknown arg $1"; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$TARGET" ]]; then
|
|
echo "ERROR: --target is required (droplet public IP + SRT port)"
|
|
exit 1
|
|
fi
|
|
|
|
MAXRATE=$(( BITRATE_K + 500 ))
|
|
BUFSIZE=$(( BITRATE_K * 2 ))
|
|
|
|
echo "==> Hotel SRT push"
|
|
echo " target: srt://${TARGET}?streamid=${STREAMID}"
|
|
echo " bitrate: ${BITRATE_K}k (max ${MAXRATE}k)"
|
|
echo " resolution:${RES} @ ${FRAMERATE}fps"
|
|
echo " devices: video=${VIDEO_DEV} audio=${AUDIO_DEV}"
|
|
echo
|
|
echo "Hit Ctrl-C to stop. The droplet will keep the last good frame if packets are lost."
|
|
|
|
exec ffmpeg \
|
|
-f avfoundation \
|
|
-framerate "${FRAMERATE}" \
|
|
-video_size "${RES}" \
|
|
-i "${VIDEO_DEV}:${AUDIO_DEV}" \
|
|
-c:v libx264 -preset veryfast -tune zerolatency \
|
|
-b:v "${BITRATE_K}k" -maxrate "${MAXRATE}k" -bufsize "${BUFSIZE}k" \
|
|
-g $(( FRAMERATE * 2 )) \
|
|
-c:a aac -b:a 128k -ar 48000 \
|
|
-f mpegts \
|
|
"srt://${TARGET}?streamid=${STREAMID}"
|