Files
media-streamer/s3-ffmpeg-stream/entrypoint.sh
William Valentin c167c8623c first commit
2025-08-15 23:45:13 -07:00

127 lines
3.5 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# --- Configuration (from environment variables with defaults) ---
URLS="${URLS:-}"
URLS_FILE="${URLS_FILE:-/app/urls.txt}"
LOOP="${LOOP:-1}"
PROTOCOL="${PROTOCOL:-udp}" # udp | rtp | rtmp | icecast
TARGET="${TARGET:-udp://239.0.0.1:1234?ttl=16}"
CODEC="${CODEC:-aac}"
COPY_CODEC_WHEN_POSSIBLE="${COPY_CODEC_WHEN_POSSIBLE:-1}"
BITRATE="${BITRATE:-160k}"
SAMPLE_RATE="${SAMPLE_RATE:-48000}"
FFMPEG_EXTRA_ARGS="${FFMPEG_EXTRA_ARGS:-}"
# --- Globals ---
PID_FILE="/tmp/ffmpeg.pid"
PLAYLIST_FILE="/tmp/playlist.txt"
declare -a URL_LIST=()
declare -a CODEC_ARGS=()
declare -a OUTPUT_FORMAT_ARGS=()
# --- Functions ---
# Graceful cleanup on exit
cleanup() {
rm -f "$PID_FILE" "$PLAYLIST_FILE"
}
trap cleanup EXIT
# Populates URL_LIST from environment or file
get_urls() {
local raw_urls=()
if [[ -n "$URLS" ]]; then
IFS=$'\n,' read -r -d '' -a raw_urls < <(printf '%s\0' "$URLS")
elif [[ -f "$URLS_FILE" ]]; then
mapfile -t raw_urls < "$URLS_FILE"
fi
# Trim whitespace and remove empty entries
for u in "${raw_urls[@]:-}"; do
u="$(echo "$u" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
[[ -n "$u" ]] && URL_LIST+=("$u")
done
}
# Creates the playlist file for ffmpeg's concat demuxer
build_playlist() {
: > "$PLAYLIST_FILE"
for u in "${URL_LIST[@]}"; do
printf "file '%s'\n" "$u" >> "$PLAYLIST_FILE"
done
}
# Sets OUTPUT_FORMAT_ARGS based on the streaming protocol
get_output_format_args() {
case "$PROTOCOL" in
udp) OUTPUT_FORMAT_ARGS=(-f mpegts) ;;
rtp) OUTPUT_FORMAT_ARGS=(-f rtp) ;;
rtmp) OUTPUT_FORMAT_ARGS=(-f flv) ;;
icecast) OUTPUT_FORMAT_ARGS=(-content_type audio/mpeg -f mp3) ;;
*) echo "Unsupported PROTOCOL: $PROTOCOL" >&2; exit 2 ;;
esac
}
# Sets CODEC_ARGS for copying or re-encoding
get_codec_args() {
if [[ "$COPY_CODEC_WHEN_POSSIBLE" == "1" && "$PROTOCOL" != "icecast" ]]; then
CODEC_ARGS=(-c:a copy)
elif [[ "$PROTOCOL" == "icecast" ]]; then
# Icecast typically requires MP3
CODEC_ARGS=(-c:a libmp3lame -b:a "$BITRATE" -ar "$SAMPLE_RATE" -ac 2)
else
# Default re-encode
CODEC_ARGS=(-c:a "$CODEC" -b:a "$BITRATE" -ar "$SAMPLE_RATE" -ac 2)
fi
}
# Runs a single ffmpeg instance
run_ffmpeg() {
local proto_whitelist="file,crypto,data,subfile,http,https,tcp,tls,pipe"
# Run in background to allow this script to wait and manage it
ffmpeg -hide_banner -nostats -v info \
-protocol_whitelist "$proto_whitelist" \
-re -stream_loop -1 -f concat -safe 0 -i "$PLAYLIST_FILE" \
-vn "${CODEC_ARGS[@]}" \
"${OUTPUT_FORMAT_ARGS[@]}" \
$FFMPEG_EXTRA_ARGS \
"$TARGET" &
echo $! > "$PID_FILE"
wait $!
}
# --- Main Execution ---
main() {
get_urls
if [[ ${#URL_LIST[@]} -eq 0 ]]; then
echo "No URLs provided. Set URLS env or mount a file at $URLS_FILE." >&2
exit 1
fi
get_output_format_args
get_codec_args
build_playlist
echo "Starting stream from ${#URL_LIST[@]} URL(s) → $PROTOCOL$TARGET"
if [[ "$LOOP" == "1" ]]; then
while true; do
set +e # Prevent exit on non-zero ffmpeg exit code
run_ffmpeg
local exit_code=$?
set -e
echo "FFmpeg exited with code $exit_code; restarting in 2s..."
sleep 2
# Rebuild playlist in case URLs have changed/expired
build_playlist
done
else
run_ffmpeg
fi
}
main "$@"