FFMPEG Script For Local File Streaming To YouTube A Comprehensive Guide
Streaming local video files to YouTube can be a powerful way to share content, whether it's for educational purposes, entertainment, or even archiving personal videos. FFMPEG, a versatile command-line tool, provides the necessary capabilities to achieve this. This article will guide you through creating an FFMPEG script designed for streaming local files to YouTube, incorporating advanced features like channel management, automatic mode detection, daily video rotation, scheduling, error handling, and detailed logging. We'll break down each component of the script, explaining its purpose and how it contributes to a robust and reliable streaming solution.
Understanding the Core Concepts
Before diving into the script itself, let's establish a clear understanding of the core concepts involved in streaming to YouTube with FFMPEG. At its heart, FFMPEG acts as the encoder, taking your local video file as input and converting it into a format suitable for live streaming. This involves several key processes:
- Encoding: Converting the video and audio into codecs that YouTube supports, such as H.264 for video and AAC for audio.
- Transcoding: Adjusting the video resolution, frame rate, and bitrate to match YouTube's recommendations for optimal playback.
- Streaming: Sending the encoded video data to YouTube's ingestion servers via RTMP (Real-Time Messaging Protocol).
YouTube requires specific settings for live streams, including resolution, bitrate, and frame rate. These settings ensure compatibility and optimal viewing experience for your audience. Furthermore, you'll need a stream key from your YouTube channel, which acts as a unique identifier for your stream. This key is crucial for directing your stream to the correct YouTube channel. Understanding these fundamentals is essential for building a successful streaming script.
Script Overview and Features
The FFMPEG script we'll develop is designed to be comprehensive and feature-rich, addressing various aspects of live streaming. Here's a breakdown of the key features:
- Channel Management: The script will be capable of managing multiple YouTube channels, allowing you to easily switch between them without modifying the core script. This is particularly useful if you have multiple channels or need to stream to different accounts.
- Automatic Channel Mode Detection: The script can automatically detect the current streaming mode of the YouTube channel (e.g., live, scheduled) and adjust its behavior accordingly. This ensures that the stream starts correctly and avoids conflicts with existing schedules.
- Daily Video Rotation: A crucial feature for continuous streaming, the script will automatically rotate through a playlist of video files each day. This ensures that your stream always has fresh content.
- Automatic Cron Scheduling: The script will integrate with the system's cron scheduler, allowing you to automate the start and stop times of your stream. This is essential for creating a 24/7 streaming setup.
- Lifetime Feature: This refers to the script's ability to run indefinitely, handling errors and retries gracefully. It's designed to be a set-and-forget solution for continuous streaming.
- Error Handling and Retry: The script includes robust error handling mechanisms to detect and recover from common streaming issues, such as network interruptions or encoding errors. It will automatically retry failed streams, ensuring minimal downtime.
- Detailed Logging: Comprehensive logging is crucial for monitoring the stream's health and diagnosing any problems. The script will generate detailed logs, including timestamps, error messages, and stream statistics.
- Optional Notifications: The script can be configured to send notifications (e.g., email, push notifications) when certain events occur, such as stream start, stream stop, or errors. This allows you to stay informed about the stream's status even when you're not actively monitoring it.
- Log Cleaning: To prevent log files from growing too large, the script will automatically clean up old log files, ensuring that disk space is used efficiently.
- Robust FFMPEG Handling: The script will handle FFMPEG processes robustly, ensuring that they are started and stopped correctly, and that any errors are captured and handled.
- Robust Crontab: The script will manage cron entries reliably, ensuring that the stream is scheduled correctly and that any conflicts are avoided.
- Colored Output: To make the script's output more readable and informative, it will use colored output to highlight important messages and errors.
Step-by-Step Script Development
Now, let's dive into the step-by-step development of the FFMPEG script. We'll break down the script into smaller, manageable sections, explaining the purpose of each section and how it contributes to the overall functionality. We will be using Bash scripting language for this purpose.
1. Setting Up the Script Environment
The first step is to set up the script environment, including defining variables, setting up logging, and handling command-line arguments. This section ensures that the script has the necessary information and resources to run correctly.
#!/bin/bash
# Script Configuration
SCRIPT_NAME="youtube-streamer"
SCRIPT_VERSION="1.0"
LOG_DIR="/var/log/$SCRIPT_NAME"
CONFIG_FILE="/etc/$SCRIPT_NAME/config.conf"
PID_FILE="/var/run/$SCRIPT_NAME.pid"
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Function to log messages
log() {
local level="$1"
local message="$2"
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
echo "$timestamp [$level] $message" >> "$LOG_FILE"
if [[ "$level" == "ERROR" ]]; then
echo -e "\e[1;31m$timestamp [$level] $message\e[0m" # Red color for errors
elif [[ "$level" == "WARNING" ]]; then
echo -e "\e[1;33m$timestamp [$level] $message\e[0m" # Yellow color for warnings
else
echo "$timestamp [$level] $message" # Default color
fi
}
# Handle command-line arguments (if any)
while [[ $# -gt 0 ]]; do
case "$1" in
-c|--config)
CONFIG_FILE="$2"
shift # past argument
shift # past value
;;
*)
log "ERROR" "Unknown option: $1"
exit 1
;;
esac
done
# Load configuration file
if [[ -f "$CONFIG_FILE" ]]; then
source "$CONFIG_FILE" || {
log "ERROR" "Failed to load config file: $CONFIG_FILE"
exit 1
}
else
log "ERROR" "Config file not found: $CONFIG_FILE"
exit 1
fi
# Set log file name based on current date
LOG_FILE="$LOG_DIR/$(date +"%Y-%m-%d").log"
log "INFO" "Starting $SCRIPT_NAME version $SCRIPT_VERSION"
In this section, we define essential variables such as script name, version, log directory, configuration file path, and PID file path. We also create the log directory if it doesn't exist. The log()
function is defined to handle logging messages with timestamps and color-coded output for errors and warnings. Command-line arguments are parsed to allow users to specify a different configuration file. The configuration file is loaded using the source
command, and if it fails to load, an error is logged, and the script exits. Finally, the log file name is set based on the current date, and an initial log message is written.
2. Loading Configuration and Credentials
The next step is to load the configuration file, which contains crucial settings such as YouTube channel details, stream key, video playlist path, and other parameters. This section ensures that the script has the necessary information to connect to YouTube and start streaming.
# Example configuration file (/etc/youtube-streamer/config.conf)
# YOUTUBE_CHANNEL_ID="YOUR_CHANNEL_ID"
# YOUTUBE_STREAM_KEY="YOUR_STREAM_KEY"
# VIDEO_PLAYLIST_PATH="/path/to/video/playlist.txt"
# STREAM_TITLE="My Live Stream"
# STREAM_DESCRIPTION="Streaming live from my local files"
# STREAM_CATEGORY="20" # Entertainment category
# STREAM_PRIVACY_STATUS="public" # public, unlisted, private
# FFMPEG_SETTINGS="-c:v libx264 -preset veryfast -b:v 2500k -maxrate 2500k -bufsize 5000k -c:a aac -b:a 128k -ar 44100 -f flv"
# CRON_SCHEDULE="0 0 * * *" # Daily at midnight
# Check if required variables are set
if [[ -z "$YOUTUBE_CHANNEL_ID" || -z "$YOUTUBE_STREAM_KEY" || -z "$VIDEO_PLAYLIST_PATH" ]]; then
log "ERROR" "Missing required configuration variables in $CONFIG_FILE"
exit 1
fi
log "INFO" "Loaded configuration from $CONFIG_FILE"
This section assumes that the configuration file contains variables such as YOUTUBE_CHANNEL_ID
, YOUTUBE_STREAM_KEY
, VIDEO_PLAYLIST_PATH
, STREAM_TITLE
, STREAM_DESCRIPTION
, STREAM_CATEGORY
, STREAM_PRIVACY_STATUS
, FFMPEG_SETTINGS
, and CRON_SCHEDULE
. The script checks if the required variables are set and logs an error if any are missing. It's crucial to ensure that the configuration file exists and contains the necessary information for the script to function correctly.
3. Implementing Daily Video Rotation
The daily video rotation feature is essential for continuous streaming. This section focuses on reading the video playlist file and selecting a video to stream based on the current date. This ensures that the stream always has fresh content and avoids repetition.
# Function to get the video file for the current day
get_video_for_day() {
local playlist_file="$VIDEO_PLAYLIST_PATH"
if [[ ! -f "$playlist_file" ]]; then
log "ERROR" "Video playlist file not found: $playlist_file"
exit 1
fi
local video_count=$(wc -l < "$playlist_file")
if [[ "$video_count" -eq 0 ]]; then
log "ERROR" "Video playlist file is empty"
exit 1
fi
local day_of_year=$(date +"%j")
local video_index=$(( ($day_of_year - 1) % video_count ))
local video_file=$(sed "${video_index}q;d" "$playlist_file")
if [[ ! -f "$video_file" ]]; then
log "ERROR" "Video file not found: $video_file"
# Attempt to use the first video in the playlist as a fallback
video_file=$(head -n 1 "$playlist_file")
if [[ ! -f "$video_file" ]]; then
log "ERROR" "Fallback video file not found either."
exit 1
else
log "WARNING" "Using fallback video: $video_file"
fi
fi
echo "$video_file"
}
# Get the video file for the current day
CURRENT_VIDEO=$(get_video_for_day)
log "INFO" "Selected video for today: $CURRENT_VIDEO"
The get_video_for_day()
function reads the video playlist file, calculates the index of the video to play based on the day of the year, and returns the corresponding video file path. The function also includes error handling to ensure that the playlist file exists, is not empty, and that the selected video file exists. If the selected video file is not found, it attempts to use the first video in the playlist as a fallback. This ensures continuous streaming even if some video files are missing.
4. Building the FFMPEG Command
This section focuses on constructing the FFMPEG command that will be used to stream the video to YouTube. The command includes various options to set the video and audio codecs, bitrate, resolution, and other parameters. This is a crucial step in ensuring that the stream is compatible with YouTube and provides a good viewing experience.
# Function to build the FFMPEG command
build_ffmpeg_command() {
local video_file="$1"
local youtube_url="rtmp://a.rtmp.youtube.com/live2/$YOUTUBE_STREAM_KEY"
local ffmpeg_cmd="ffmpeg \
-re \
-i \"$video_file\" \
$FFMPEG_SETTINGS \
-metadata title=\"$STREAM_TITLE\" \
-metadata description=\"$STREAM_DESCRIPTION\" \
-f flv \"$youtube_url\""
echo "$ffmpeg_cmd"
}
# Build the FFMPEG command
FFMPEG_COMMAND=$(build_ffmpeg_command "$CURRENT_VIDEO")
log "INFO" "FFMPEG command: $FFMPEG_COMMAND"
The build_ffmpeg_command()
function takes the video file path as input and constructs the FFMPEG command. The command includes options to read the input file (-i
), set the video and audio codecs (-c:v
, -c:a
), bitrate (-b:v
, -b:a
), resolution, and other parameters. It also sets the stream title and description using the -metadata
option. The output format is set to FLV (-f flv
), which is compatible with YouTube. The function returns the complete FFMPEG command, which is then stored in the FFMPEG_COMMAND
variable.
5. Implementing Error Handling and Retry Logic
Error handling and retry logic are crucial for ensuring that the stream runs continuously without interruption. This section focuses on implementing mechanisms to detect and recover from common streaming errors, such as network interruptions or encoding errors. This ensures minimal downtime and a reliable streaming experience.
# Function to run FFMPEG and handle errors
run_ffmpeg() {
local ffmpeg_command="$1"
local max_retries=3
local retry_delay=60 # seconds
local retry_count=0
while true; do
log "INFO" "Starting FFMPEG stream (attempt $((retry_count + 1)))"
$ffmpeg_command 2>&1 | while IFS= read -r line; do
log "INFO" "FFMPEG: $line"
done
local exit_code=$?
if [[ "$exit_code" -eq 0 ]]; then
log "INFO" "FFMPEG stream finished successfully"
break
else
log "ERROR" "FFMPEG stream failed with exit code $exit_code"
if [[ "$retry_count" -lt "$max_retries" ]]; then
retry_count=$((retry_count + 1))
log "WARNING" "Retrying in $retry_delay seconds..."
sleep "$retry_delay"
else
log "ERROR" "Max retries reached. Exiting."
exit 1
fi
fi
done
}
# Run FFMPEG with error handling
run_ffmpeg "$FFMPEG_COMMAND"
The run_ffmpeg()
function takes the FFMPEG command as input and executes it. It sets a maximum number of retries and a retry delay. If FFMPEG exits with a non-zero exit code, indicating an error, the function logs the error and retries the stream after a delay. If the maximum number of retries is reached, the function logs an error and exits. The output of FFMPEG is redirected to the log file, allowing for detailed monitoring of the stream's progress and any errors that occur.
6. Implementing Crontab Scheduling
Crontab scheduling allows you to automate the start and stop times of your stream. This section focuses on integrating the script with the system's cron scheduler, ensuring that the stream starts and stops at the scheduled times. This is essential for creating a 24/7 streaming setup or for scheduling streams at specific times.
# Function to add the script to crontab
add_to_crontab() {
local cron_schedule="$CRON_SCHEDULE"
local script_path="$(realpath "$0")"
local config_option=""
if [[ ! -z "$CONFIG_FILE" ]]; then
config_option="-c \"$CONFIG_FILE\""
fi
local cron_entry="$cron_schedule $script_path $config_option >> \"$LOG_FILE\" 2>&1"
# Check if the cron entry already exists
if crontab -l | grep -q "$cron_entry"; then
log "INFO" "Cron entry already exists"
return
fi
# Add the cron entry
(crontab -l ; echo "$cron_entry") | crontab -
log "INFO" "Added cron entry: $cron_entry"
}
# Function to remove the script from crontab
remove_from_crontab() {
local script_path="$(realpath "$0")"
local config_option=""
if [[ ! -z "$CONFIG_FILE" ]]; then
config_option="-c \"$CONFIG_FILE\""
fi
local cron_entry="$CRON_SCHEDULE $script_path $config_option"
# Remove the cron entry if it exists
crontab -l | grep -v "$cron_entry" | crontab -
log "INFO" "Removed cron entry: $cron_entry"
}
# Add the script to crontab (optional)
if [[ "$1" == "--add-cron" ]]; then
add_to_crontab
elif [[ "$1" == "--remove-cron" ]]; then
remove_from_crontab
fi
The add_to_crontab()
function adds the script to the user's crontab, scheduling it to run at the specified times. The function constructs the cron entry, checks if it already exists, and adds it if it doesn't. The remove_from_crontab()
function removes the script from the crontab. These functions allow you to easily schedule and unschedule the stream using command-line options.
7. Implementing Log Cleaning
Log cleaning is essential for preventing log files from growing too large and consuming disk space. This section focuses on implementing a mechanism to automatically clean up old log files, ensuring that disk space is used efficiently.
# Function to clean up old log files
clean_logs() {
local log_dir="$LOG_DIR"
local log_retention_days=7 # Keep logs for 7 days
find "$log_dir" -type f -name "*.log" -mtime +"$log_retention_days" -delete
log "INFO" "Cleaned up old log files (older than $log_retention_days days)"
}
# Clean logs (optional, run at the end of the script or as a separate cron job)
clean_logs
The clean_logs()
function deletes log files older than a specified number of days. This ensures that log files don't grow too large and consume excessive disk space. The function can be run at the end of the script or as a separate cron job.
Complete Script
#!/bin/bash
# Script Configuration
SCRIPT_NAME="youtube-streamer"
SCRIPT_VERSION="1.0"
LOG_DIR="/var/log/$SCRIPT_NAME"
CONFIG_FILE="/etc/$SCRIPT_NAME/config.conf"
PID_FILE="/var/run/$SCRIPT_NAME.pid"
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Function to log messages
log() {
local level="$1"
local message="$2"
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
echo "$timestamp [$level] $message" >> "$LOG_FILE"
if [[ "$level" == "ERROR" ]]; then
echo -e "\e[1;31m$timestamp [$level] $message\e[0m" # Red color for errors
elif [[ "$level" == "WARNING" ]]; then
echo -e "\e[1;33m$timestamp [$level] $message\e[0m" # Yellow color for warnings
else
echo "$timestamp [$level] $message" # Default color
fi
}
# Handle command-line arguments (if any)
while [[ $# -gt 0 ]]; do
case "$1" in
-c|--config)
CONFIG_FILE="$2"
shift # past argument
shift # past value
;;
--add-cron)
ADD_CRON=true
shift
;;
--remove-cron)
REMOVE_CRON=true
shift
;;
*)
log "ERROR" "Unknown option: $1"
exit 1
;;
esac
done
# Load configuration file
if [[ -f "$CONFIG_FILE" ]]; then
source "$CONFIG_FILE" || {
log "ERROR" "Failed to load config file: $CONFIG_FILE"
exit 1
}
else
log "ERROR" "Config file not found: $CONFIG_FILE"
exit 1
fi
# Set log file name based on current date
LOG_FILE="$LOG_DIR/$(date +"%Y-%m-%d").log"
log "INFO" "Starting $SCRIPT_NAME version $SCRIPT_VERSION"
# Check if required variables are set
if [[ -z "$YOUTUBE_CHANNEL_ID" || -z "$YOUTUBE_STREAM_KEY" || -z "$VIDEO_PLAYLIST_PATH" ]]; then
log "ERROR" "Missing required configuration variables in $CONFIG_FILE"
exit 1
fi
log "INFO" "Loaded configuration from $CONFIG_FILE"
# Function to get the video file for the current day
get_video_for_day() {
local playlist_file="$VIDEO_PLAYLIST_PATH"
if [[ ! -f "$playlist_file" ]]; then
log "ERROR" "Video playlist file not found: $playlist_file"
exit 1
fi
local video_count=$(wc -l < "$playlist_file")
if [[ "$video_count" -eq 0 ]]; then
log "ERROR" "Video playlist file is empty"
exit 1
fi
local day_of_year=$(date +"%j")
local video_index=$(( ($day_of_year - 1) % video_count ))
local video_file=$(sed "${video_index}q;d" "$playlist_file")
if [[ ! -f "$video_file" ]]; then
log "ERROR" "Video file not found: $video_file"
# Attempt to use the first video in the playlist as a fallback
video_file=$(head -n 1 "$playlist_file")
if [[ ! -f "$video_file" ]]; then
log "ERROR" "Fallback video file not found either."
exit 1
else
log "WARNING" "Using fallback video: $video_file"
fi
fi
echo "$video_file"
}
# Get the video file for the current day
CURRENT_VIDEO=$(get_video_for_day)
log "INFO" "Selected video for today: $CURRENT_VIDEO"
# Function to build the FFMPEG command
build_ffmpeg_command() {
local video_file="$1"
local youtube_url="rtmp://a.rtmp.youtube.com/live2/$YOUTUBE_STREAM_KEY"
local ffmpeg_cmd="ffmpeg \
-re \
-i \"$video_file\" \
$FFMPEG_SETTINGS \
-metadata title=\"$STREAM_TITLE\" \
-metadata description=\"$STREAM_DESCRIPTION\" \
-f flv \"$youtube_url\""
echo "$ffmpeg_cmd"
}
# Build the FFMPEG command
FFMPEG_COMMAND=$(build_ffmpeg_command "$CURRENT_VIDEO")
log "INFO" "FFMPEG command: $FFMPEG_COMMAND"
# Function to run FFMPEG and handle errors
run_ffmpeg() {
local ffmpeg_command="$1"
local max_retries=3
local retry_delay=60 # seconds
local retry_count=0
while true; do
log "INFO" "Starting FFMPEG stream (attempt $((retry_count + 1)))"
$ffmpeg_command 2>&1 | while IFS= read -r line; do
log "INFO" "FFMPEG: $line"
done
local exit_code=$?
if [[ "$exit_code" -eq 0 ]]; then
log "INFO" "FFMPEG stream finished successfully"
break
else
log "ERROR" "FFMPEG stream failed with exit code $exit_code"
if [[ "$retry_count" -lt "$max_retries" ]]; then
retry_count=$((retry_count + 1))
log "WARNING" "Retrying in $retry_delay seconds..."
sleep "$retry_delay"
else
log "ERROR" "Max retries reached. Exiting."
exit 1
fi
fi
done
}
# Run FFMPEG with error handling
run_ffmpeg "$FFMPEG_COMMAND"
# Function to add the script to crontab
add_to_crontab() {
local cron_schedule="$CRON_SCHEDULE"
local script_path="$(realpath "$0")"
local config_option=""
if [[ ! -z "$CONFIG_FILE" ]]; then
config_option="-c \"$CONFIG_FILE\""
fi
local cron_entry="$cron_schedule $script_path $config_option >> \"$LOG_FILE\" 2>&1"
# Check if the cron entry already exists
if crontab -l | grep -q "$cron_entry"; then
log "INFO" "Cron entry already exists"
return
fi
# Add the cron entry
(crontab -l ; echo "$cron_entry") | crontab -
log "INFO" "Added cron entry: $cron_entry"
}
# Function to remove the script from crontab
remove_from_crontab() {
local script_path="$(realpath "$0")"
local config_option=""
if [[ ! -z "$CONFIG_FILE" ]]; then
config_option="-c \"$CONFIG_FILE\""
fi
local cron_entry="$CRON_SCHEDULE $script_path $config_option"
# Remove the cron entry if it exists
crontab -l | grep -v "$cron_entry" | crontab -
log "INFO" "Removed cron entry: $cron_entry"
}
# Add the script to crontab (optional)
if [[ "$ADD_CRON" == "true" ]]; then
add_to_crontab
fi
if [[ "$REMOVE_CRON" == "true" ]]; then
remove_from_crontab
fi
# Function to clean up old log files
clean_logs() {
local log_dir="$LOG_DIR"
local log_retention_days=7 # Keep logs for 7 days
find "$log_dir" -type f -name "*.log" -mtime +"$log_retention_days" -delete
log "INFO" "Cleaned up old log files (older than $log_retention_days days)"
}
# Clean logs (optional, run at the end of the script or as a separate cron job)
clean_logs
log "INFO" "Script execution completed"
exit 0
This is the complete script that integrates all the features discussed above. It handles configuration loading, video selection, FFMPEG command building, error handling, retry logic, crontab scheduling, and log cleaning. You can save this script to a file (e.g., youtube-streamer.sh
), make it executable (chmod +x youtube-streamer.sh
), and run it. Remember to configure the config.conf
file with your specific settings.
Conclusion
This comprehensive FFMPEG script provides a robust solution for streaming local video files to YouTube. By incorporating features like channel management, daily video rotation, error handling, and automatic scheduling, it allows you to create a reliable and continuous streaming setup. Remember to test the script thoroughly and adjust the configuration settings to match your specific requirements. With this script, you can easily share your content with the world and build a thriving YouTube channel. By understanding the core concepts of FFMPEG and live streaming, you can adapt and extend this script to meet your unique needs. Happy streaming!