I've hit a snag with my MediaMTX setup while attempting to initiate a fallback stream using FFmpeg with the runOnInit directive. It appears that the environment variables $RTSP_PORT and $MTX_PATH aren't initialized when runOnInit triggers the command.
My goal is to have MediaMTX manage RTSP streams and establish a fallback stream through FFmpeg that kicks in automatically upon MediaMTX startup. The aim is to seamlessly switch to the fallback stream if any of the primary streams encounter issues.
Expected Behavior:
Here's a simplified version of my YAML configuration:
fallbackpath:
runOnInit: ffmpeg -re -stream_loop -1 -i "/path/to/fallback/video.mp4" -c:v copy -an -f rtsp "rtsp://127.0.0.1:$RTSP_PORT/$MTX_PATH"
runOnInitRestart: yes
paths:
# Primary streams
stream1:
source: rtsp://example.com/stream1
fallback: fallbackpath
stream2:
source: rtsp://example.com/stream2
fallback: fallbackpath
# More streams...
However, it seems that $RTSP_PORT and $MTX_PATH aren't available when runOnInit is triggered, resulting in errors.
I've attempted using the runOnDemand directive as an alternative, but I'm uncertain if it's suitable for my scenario. Unfortunately, both runOnInit and runOnDemand configurations fail, causing MediaMTX to abort.
I'd appreciate any guidance on ensuring that the environment variables are properly initialized when starting the fallback stream.
Thanks for your assistance!
Probably not the cleanest option and should probably be using MediaMTX built in "fallback:" just don't know how, so scripted my own version using Bash script FFMpeg and FFProbe, the way I figure it if it is on a cron for every 2-5 seconds it will do the trick just fine:
#!/bin/bash
# Function to extract specific variables from .env file
extractEnvVariable() {
local envFile="${1}"
local variableName="${2}"
local variableValue=$(grep "^${variableName}=" "$envFile" | cut -d'=' -f2- | tr -d '"' | tr -d "'")
echo "$variableValue"
}
# Absolute path to the script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Paths to the .env and .env-secrets files
ENV_FILE="$SCRIPT_DIR/../.env"
# Define paths and variables
pathsConfigPath="$SCRIPT_DIR/../config/paths_config.yml"
mediamtxConfigPath="/usr/local/etc/mediamtx.yml"
streamsDirectory="$SCRIPT_DIR/../config/customers/"
LOG_FILE="/var/log/stream_conductor.log"
# Get FFProbe path, ffmpeg path, fallback URL, and stream URI from .env
FFPROBE=$(extractEnvVariable "$ENV_FILE" "FFMPEG_FFPROBE_PATH")
FFMPEG=$(extractEnvVariable "$ENV_FILE" "FFMPEG_FFMPEG_PATH")
FALLBACK=$(extractEnvVariable "$ENV_FILE" "MEDIAMTX_FALLBACK")
STREAM_URI=$(extractEnvVariable "$ENV_FILE" "MEDIAMTX_URL")
STREAM_URI=${STREAM_URI%/} # Trim trailing forward slash
# Trim trailing forward slash from STREAM_URI
STREAM_URI=${STREAM_URI%/}
# Clear log file
> "$LOG_FILE"
# Check if streams directory exists and is a directory
if [ -d "$streamsDirectory" ]; then
configContent=""
# Iterate through customer directories
customerDirectories=$(ls "$streamsDirectory")
for customerDirectory in $customerDirectories; do
if [ "$customerDirectory" != "." ] && [ "$customerDirectory" != ".." ]; then
customerPath="$streamsDirectory/$customerDirectory"
# Check if customer directory exists and is a directory
if [ -d "$customerPath" ]; then
streamPath="$customerPath/streams/"
# Check if streams directory exists and is a directory
if [ -d "$streamPath" ]; then
# Loop through stream files
for streamFile in "$streamPath"/*; do
if [ "$streamFile" != "$streamPath/*" ]; then
UUID=$(basename "$streamFile" | cut -c7-) # Assuming stream directory names start with "stream"
rtspUrlFile="$streamFile/rtsp_url_$UUID.txt"
if [ -f "$rtspUrlFile" ]; then
rtspUrl=$(<"$rtspUrlFile")
# Check if RTSP stream is active using FFProbe
if $FFPROBE -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 "$rtspUrl" >/dev/null 2>&1; then
# Stream is active, use original RTSP URL
configContent+=" $UUID:\n"
configContent+=" source: $rtspUrl\n"
else
# Stream is not active, fallback to tsdkfallback
configContent+=" $UUID:\n"
configContent+=" source: $STREAM_URI/tsdkfallback\n"
fi
fi
fi
done
else
echo "Streams directory not found or is not a directory: $streamPath" >> "$LOG_FILE"
fi
else
echo "Customer directory not found or is not a directory: $customerPath" >> "$LOG_FILE"
fi
fi
done
# Add indentation for tsdkfallback entry
configContent+=" tsdkfallback:\n"
else
echo "Streams directory not found or is not a directory: $streamsDirectory" >> "$LOG_FILE"
fi
# Write the updated content to paths_config.yml
echo -e "$configContent" > "$pathsConfigPath"
# Load original configuration from mediamtx.yml
mediamtxConfig=$(<"$mediamtxConfigPath")
# Combine mediamtx.yml and configContent
combinedConfig="$mediamtxConfig\npaths:\n$configContent"
# Write the combined configuration to config.yml
outputFilePath="/usr/local/etc/config.yml"
echo -e "$combinedConfig" > "$outputFilePath"
# Check if MediaMTX process is not already running
if ! pgrep -x "mediamtx" >/dev/null; then
# Start MediaMTX
/usr/local/bin/mediamtx "$outputFilePath" &
fi
# Check if ffmpeg process is not already running
if ! pgrep -x "ffmpeg" >/dev/null; then
# Execute ffmpeg command as a standalone process
"$FFMPEG" -re -stream_loop -1 -i "$FALLBACK" -c:v copy -an -f rtsp "$STREAM_URI/tsdkfallback" &
fi
This then outputs this into the config.yml assuming in this particular instance none of the cameras are streaming so it has defaulted all the stream sources to the fallback:
paths:
71fc528a-a65d-473e-ac9f-ff4751576cce:
source: rtsp://localhost:8554/tsdkfallback
a34dd062-087d-4a68-99d0-612bc4478910:
source: rtsp://localhost:8554/tsdkfallback
ab1ac337-e137-4ea1-a4a8-34843abeeffe:
source: rtsp://localhost:8554/tsdkfallback
add118ab-1f19-45ce-913d-59e242de3e08:
source: rtsp://localhost:8554/tsdkfallback
c5908bd6-9da6-44b2-8ffa-57af78ed01bd:
source: rtsp://localhost:8554/tsdkfallback
dda7a9cb-e348-44ce-98f7-cb224c8e4a9d:
source: rtsp://localhost:8554/tsdkfallback
tsdkfallback:
I tested this streaming to VLC App and it works