#!/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"