ShowerLoop-cc/docker/showerloop/public/transcode-videos.sh

120 lines
3.9 KiB
Bash
Executable File

#!/bin/bash
set -e
# Base directories
VIDEO_SOURCE_DIR="static/videos"
HLS_OUTPUT_DIR="static/videos/hls"
POSTER_OUTPUT_DIR="static/videos/posters"
# Create output directories if they don't exist
mkdir -p "$HLS_OUTPUT_DIR"
mkdir -p "$POSTER_OUTPUT_DIR"
# HLS Settings
SEGMENT_TIME=4 # Segment duration in seconds
KEYFRAME_INTERVAL=48 # Keyframe interval (2 seconds at 24fps)
# Resolutions for adaptive streaming
RESOLUTIONS=(
"256:144" # 144p
"426:240" # 240p
"640:360" # 360p
"854:480" # 480p
"1280:720" # 720p
)
# Bitrates for each resolution (in kbps)
BITRATES=(
"200k" # 144p
"400k" # 240p
"800k" # 360p
"1500k" # 480p
"2500k" # 720p
)
# Function to generate HLS streams for a video
transcode_to_hls() {
local input_file="$1"
local video_id=$(basename "$input_file" .mp4)
local output_dir="$HLS_OUTPUT_DIR/$video_id"
echo "Transcoding $input_file to HLS format..."
# Create output directory
mkdir -p "$output_dir"
# Generate variant playlists for each resolution
for i in "${!RESOLUTIONS[@]}"; do
local resolution=${RESOLUTIONS[$i]}
local bitrate=${BITRATES[$i]}
local width=$(echo $resolution | cut -d':' -f1)
local height=$(echo $resolution | cut -d':' -f2)
echo " Creating $height stream ($bitrate)..."
# Using better encoding settings for quality and efficiency
ffmpeg -y -i "$input_file" \
-vf "scale=$resolution:force_original_aspect_ratio=decrease,pad=$resolution:-1:-1:color=black" \
-c:v libx264 -preset slower -crf 22 -maxrate $bitrate -bufsize $(echo $bitrate | sed 's/k/000/') \
-g $KEYFRAME_INTERVAL -keyint_min $KEYFRAME_INTERVAL \
-sc_threshold 0 -hls_time $SEGMENT_TIME \
-hls_playlist_type vod \
-hls_segment_filename "$output_dir/${video_id}_${height}p_%03d.ts" \
-master_pl_name "master.m3u8" \
-var_stream_map "v:0,name:${height}p" \
-c:a aac -b:a 128k \
"$output_dir/${video_id}_${height}p.m3u8"
done
# Create master playlist
echo "#EXTM3U" > "$output_dir/master.m3u8"
echo "#EXT-X-VERSION:3" >> "$output_dir/master.m3u8"
for i in "${!RESOLUTIONS[@]}"; do
local resolution=${RESOLUTIONS[$i]}
local bitrate=${BITRATES[$i]}
local width=$(echo $resolution | cut -d':' -f1)
local height=$(echo $resolution | cut -d':' -f2)
echo "#EXT-X-STREAM-INF:BANDWIDTH=$(echo $bitrate | sed 's/k/000/'),RESOLUTION=$resolution" >> "$output_dir/master.m3u8"
echo "${video_id}_${height}p.m3u8" >> "$output_dir/master.m3u8"
done
# Create a symlink for index.m3u8
ln -sf "master.m3u8" "$output_dir/index.m3u8"
echo " Transcoding complete for $video_id"
}
# Function to generate WebP thumbnails
generate_thumbnails() {
local input_file="$1"
local video_id=$(basename "$input_file" .mp4)
echo "Generating thumbnails for $input_file..."
# Generate thumbnail at 25% of the video duration
ffmpeg -y -i "$input_file" -ss $(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$input_file" | awk '{print $1/4}') \
-vframes 1 -vf "scale=640:-1" "$POSTER_OUTPUT_DIR/${video_id}.jpg"
# Convert to WebP with better quality
ffmpeg -y -i "$POSTER_OUTPUT_DIR/${video_id}.jpg" -vf "scale=640:-1" -quality 90 "$POSTER_OUTPUT_DIR/${video_id}.webp"
# Copy thumbnails to HLS directory for reference
cp "$POSTER_OUTPUT_DIR/${video_id}.webp" "$HLS_OUTPUT_DIR/$video_id/poster.webp"
cp "$POSTER_OUTPUT_DIR/${video_id}.jpg" "$HLS_OUTPUT_DIR/$video_id/poster.jpg"
echo " Thumbnail generation complete for $video_id"
}
# Process each video
for video_file in $VIDEO_SOURCE_DIR/*.mp4; do
if [ -f "$video_file" ]; then
transcode_to_hls "$video_file"
generate_thumbnails "$video_file"
fi
done
echo "All videos have been transcoded to HLS format and thumbnails generated."
echo "HLS streams are available in $HLS_OUTPUT_DIR"
echo "Thumbnails are available in $POSTER_OUTPUT_DIR"