351 lines
12 KiB
Bash
Executable File
351 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Script to compress WebM files using VP9 codec
|
|
# Usage: ./webm_compress.sh [directory_path] [compression_level] [audio_mode] [fps]
|
|
|
|
# Function to display usage
|
|
usage() {
|
|
echo "Usage: $0 [directory_path] [compression_level] [audio_mode] [fps]"
|
|
echo " directory_path: Path to directory containing WebM files (default: current directory)"
|
|
echo " compression_level: Compression level 3-5 (default: 3)"
|
|
echo " - 3: Lossless, best compression (default)"
|
|
echo " - 4: Lossy, high quality (CRF 23)"
|
|
echo " - 5: Lossy, maximum compression (CRF 35)"
|
|
echo " audio_mode: Audio channel mode (default: stereo)"
|
|
echo " - stereo: Keep stereo audio (2 channels)"
|
|
echo " - mono: Convert to mono audio (1 channel, smaller files)"
|
|
echo " fps: Target frame rate (default: 30)"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 # Current directory, lossless, stereo, 30fps"
|
|
echo " $0 /path/to/files # Custom directory, defaults for other params"
|
|
echo " $0 /path/to/files 4 # High quality lossy compression"
|
|
echo " $0 /path/to/files 4 mono # High quality + mono audio"
|
|
echo " $0 /path/to/files 4 mono 15 # High quality + mono + 15fps"
|
|
echo " $0 /path/to/files 5 stereo 24 # Max compression + stereo + 24fps"
|
|
echo ""
|
|
echo "This script will:"
|
|
echo " - Find all .webm files in the specified directory"
|
|
echo " - Compress using VP9 codec with specified quality level"
|
|
echo " - Preserve alpha transparency when present"
|
|
echo " - Adjust frame rate and audio channels as specified"
|
|
echo " - Save results in a 'webm_compressed' subdirectory"
|
|
exit 1
|
|
}
|
|
|
|
# Function to format file size in human-readable format
|
|
format_size() {
|
|
local size=$1
|
|
if command -v numfmt &> /dev/null; then
|
|
numfmt --to=iec "$size"
|
|
else
|
|
# Fallback function for systems without numfmt
|
|
if [ "$size" -lt 1024 ]; then
|
|
echo "${size}B"
|
|
elif [ "$size" -lt 1048576 ]; then
|
|
echo "$(( size / 1024 ))K"
|
|
elif [ "$size" -lt 1073741824 ]; then
|
|
echo "$(( size / 1048576 ))M"
|
|
else
|
|
echo "$(( size / 1073741824 ))G"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Check if ffmpeg is installed
|
|
if ! command -v ffmpeg &> /dev/null; then
|
|
echo "Error: ffmpeg is not installed or not in PATH"
|
|
echo "Please install ffmpeg to use this script"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if libvpx decoders are available (needed for alpha channel support)
|
|
LIBVPX_CHECK=$(ffmpeg -decoders 2>/dev/null | grep libvpx)
|
|
if [ -z "$LIBVPX_CHECK" ]; then
|
|
echo "Warning: libvpx decoders not found. Alpha channels may not be preserved properly."
|
|
echo "Your ffmpeg installation may not support VP8/VP9 alpha transparency."
|
|
echo ""
|
|
fi
|
|
|
|
# Check if bc is available (needed for floating point calculations)
|
|
if ! command -v bc &> /dev/null; then
|
|
echo "Error: bc (basic calculator) is not installed or not in PATH"
|
|
echo "Please install bc to use this script"
|
|
exit 1
|
|
fi
|
|
|
|
# Set default values
|
|
DIRECTORY="."
|
|
COMPRESSION=3
|
|
AUDIO_MODE="stereo"
|
|
FPS=30
|
|
|
|
# Parse arguments
|
|
if [ $# -eq 0 ]; then
|
|
DIRECTORY="."
|
|
elif [ $# -eq 1 ]; then
|
|
DIRECTORY="$1"
|
|
elif [ $# -eq 2 ]; then
|
|
DIRECTORY="$1"
|
|
COMPRESSION="$2"
|
|
elif [ $# -eq 3 ]; then
|
|
DIRECTORY="$1"
|
|
COMPRESSION="$2"
|
|
AUDIO_MODE="$3"
|
|
elif [ $# -eq 4 ]; then
|
|
DIRECTORY="$1"
|
|
COMPRESSION="$2"
|
|
AUDIO_MODE="$3"
|
|
FPS="$4"
|
|
else
|
|
echo "Error: Too many arguments"
|
|
usage
|
|
fi
|
|
|
|
# Validate compression parameter
|
|
if ! [[ "$COMPRESSION" =~ ^[3-5]$ ]]; then
|
|
echo "Error: compression level must be between 3-5"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate audio mode parameter
|
|
if [ "$AUDIO_MODE" != "stereo" ] && [ "$AUDIO_MODE" != "mono" ]; then
|
|
echo "Error: audio_mode must be 'stereo' or 'mono'"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate fps parameter
|
|
if ! [[ "$FPS" =~ ^[0-9]+(\.[0-9]+)?$ ]] || (( $(echo "$FPS <= 0" | bc -l) )); then
|
|
echo "Error: fps must be a positive number"
|
|
exit 1
|
|
fi
|
|
|
|
# Set compression values based on level
|
|
case $COMPRESSION in
|
|
3) VP9_PARAMS="-c:v libvpx-vp9 -lossless 1 -cpu-used 0"; COMP_DESC="Lossless, best compression" ;;
|
|
4) VP9_PARAMS="-c:v libvpx-vp9 -crf 23 -b:v 0 -cpu-used 1"; COMP_DESC="Lossy, high quality (CRF 23)" ;;
|
|
5) VP9_PARAMS="-c:v libvpx-vp9 -crf 35 -b:v 0 -cpu-used 2"; COMP_DESC="Lossy, maximum compression (CRF 35)" ;;
|
|
esac
|
|
|
|
# Set audio parameters based on mode
|
|
if [ "$AUDIO_MODE" = "mono" ]; then
|
|
AUDIO_PARAMS="-c:a libopus -ac 1 -b:a 64k"
|
|
AUDIO_DESC="mono (64k)"
|
|
else
|
|
AUDIO_PARAMS="-c:a libopus -ac 2 -b:a 128k"
|
|
AUDIO_DESC="stereo (128k)"
|
|
fi
|
|
|
|
# Check if directory exists
|
|
if [ ! -d "$DIRECTORY" ]; then
|
|
echo "Error: Directory '$DIRECTORY' does not exist"
|
|
exit 1
|
|
fi
|
|
|
|
# Convert to absolute path
|
|
DIRECTORY=$(realpath "$DIRECTORY")
|
|
echo "Processing WebM files in: $DIRECTORY"
|
|
echo "Compression: Level $COMPRESSION ($COMP_DESC)"
|
|
echo "Audio: $AUDIO_DESC"
|
|
echo "Target FPS: $FPS"
|
|
|
|
# Create compressed output directory
|
|
COMPRESSED_DIR="$DIRECTORY/webm_compressed"
|
|
if [ ! -d "$COMPRESSED_DIR" ]; then
|
|
mkdir -p "$COMPRESSED_DIR"
|
|
echo "Created output directory: $COMPRESSED_DIR"
|
|
else
|
|
echo "Using existing output directory: $COMPRESSED_DIR"
|
|
fi
|
|
|
|
# Find all WebM files in the directory
|
|
WEBM_FILES=($(find "$DIRECTORY" -maxdepth 1 -name "*.webm" -type f))
|
|
|
|
# Check if any WebM files were found
|
|
if [ ${#WEBM_FILES[@]} -eq 0 ]; then
|
|
echo "No WebM files found in '$DIRECTORY'"
|
|
exit 0
|
|
fi
|
|
|
|
echo "Found ${#WEBM_FILES[@]} WebM file(s)"
|
|
echo "Note: Existing compressed files will be overwritten"
|
|
echo ""
|
|
|
|
# Counter for processed files
|
|
PROCESSED=0
|
|
FAILED=0
|
|
OVERWRITTEN=0
|
|
NEW_FILES=0
|
|
TOTAL_ORIGINAL_SIZE=0
|
|
TOTAL_COMPRESSED_SIZE=0
|
|
|
|
# Process each WebM file
|
|
for webm_file in "${WEBM_FILES[@]}"; do
|
|
# Get the base filename without path and extension
|
|
base_name=$(basename "$webm_file" .webm)
|
|
|
|
# Output compressed WebM file path
|
|
compressed_output="$COMPRESSED_DIR/${base_name}.webm"
|
|
|
|
echo "Processing: $(basename "$webm_file")"
|
|
|
|
# Get original file size
|
|
ORIGINAL_SIZE=$(stat -f%z "$webm_file" 2>/dev/null || stat -c%s "$webm_file" 2>/dev/null)
|
|
TOTAL_ORIGINAL_SIZE=$((TOTAL_ORIGINAL_SIZE + ORIGINAL_SIZE))
|
|
|
|
# Check if output file already exists
|
|
FILE_EXISTS=false
|
|
if [ -f "$compressed_output" ]; then
|
|
echo " → Overwriting existing file: ${base_name}.webm"
|
|
FILE_EXISTS=true
|
|
fi
|
|
|
|
# Detect codec type (VP8 or VP9) and other properties
|
|
CODEC_INFO=$(ffprobe -v quiet -select_streams v:0 -show_entries stream=codec_name -of csv=p=0 "$webm_file" 2>/dev/null)
|
|
PIXEL_FORMAT=$(ffprobe -v quiet -select_streams v:0 -show_entries stream=pix_fmt -of csv=p=0 "$webm_file" 2>/dev/null)
|
|
HAS_ALPHA=$(echo "$PIXEL_FORMAT" | grep -iE "(yuva|rgba|gbra|argb|abgr|bgra|a444|a422|a420)")
|
|
|
|
# Check for alpha_mode metadata (more reliable for WebM)
|
|
ALPHA_MODE=$(ffprobe -v quiet -select_streams v:0 -show_entries stream_tags=alpha_mode -of csv=p=0 "$webm_file" 2>/dev/null)
|
|
|
|
echo " → Original codec: $CODEC_INFO (format: $PIXEL_FORMAT)"
|
|
|
|
# Set the appropriate decoder for alpha preservation
|
|
case $CODEC_INFO in
|
|
"vp8")
|
|
DECODER_PARAMS="-c:v libvpx"
|
|
echo " → Using libvpx decoder for VP8"
|
|
;;
|
|
"vp9")
|
|
DECODER_PARAMS="-c:v libvpx-vp9"
|
|
echo " → Using libvpx-vp9 decoder for VP9"
|
|
;;
|
|
*)
|
|
DECODER_PARAMS=""
|
|
echo " → Using default decoder for $CODEC_INFO"
|
|
;;
|
|
esac
|
|
|
|
if [ -n "$HAS_ALPHA" ] || [ "$ALPHA_MODE" = "1" ]; then
|
|
echo " → Alpha channel detected, preserving transparency"
|
|
# For alpha content, use special VP9 settings and let ffmpeg choose pixel format
|
|
case $COMPRESSION in
|
|
3)
|
|
# Lossless doesn't work well with alpha in VP9, use very high quality instead
|
|
ALPHA_VP9_PARAMS="-c:v libvpx-vp9 -crf 0 -b:v 0 -cpu-used 0"
|
|
COMP_DESC_ALPHA="Near-lossless (CRF 0) for alpha preservation"
|
|
;;
|
|
4)
|
|
ALPHA_VP9_PARAMS="-c:v libvpx-vp9 -crf 23 -b:v 0 -cpu-used 1"
|
|
COMP_DESC_ALPHA="Lossy, high quality (CRF 23)"
|
|
;;
|
|
5)
|
|
ALPHA_VP9_PARAMS="-c:v libvpx-vp9 -crf 35 -b:v 0 -cpu-used 2"
|
|
COMP_DESC_ALPHA="Lossy, maximum compression (CRF 35)"
|
|
;;
|
|
esac
|
|
# Don't force pixel format for alpha - let ffmpeg auto-detect
|
|
ALPHA_PARAMS=""
|
|
VIDEO_FILTER="fps=$FPS"
|
|
FINAL_VP9_PARAMS="$ALPHA_VP9_PARAMS"
|
|
echo " → Using alpha-compatible settings: $COMP_DESC_ALPHA"
|
|
HAS_ALPHA_CONTENT=true
|
|
else
|
|
echo " → No alpha channel detected"
|
|
ALPHA_PARAMS="-pix_fmt yuv420p"
|
|
VIDEO_FILTER="fps=$FPS"
|
|
FINAL_VP9_PARAMS="$VP9_PARAMS"
|
|
HAS_ALPHA_CONTENT=false
|
|
echo " → Using standard VP9 compression"
|
|
fi
|
|
|
|
echo " → Compressing with VP9..."
|
|
|
|
# Compress the WebM file - use decoder params BEFORE input for alpha preservation
|
|
if ffmpeg $DECODER_PARAMS -i "$webm_file" \
|
|
-vf "$VIDEO_FILTER" \
|
|
$FINAL_VP9_PARAMS \
|
|
$ALPHA_PARAMS \
|
|
$AUDIO_PARAMS \
|
|
-threads 0 \
|
|
-row-mt 1 \
|
|
-y \
|
|
"$compressed_output" \
|
|
-v quiet -stats 2>/dev/null; then
|
|
|
|
# Get compressed file size
|
|
COMPRESSED_SIZE=$(stat -f%z "$compressed_output" 2>/dev/null || stat -c%s "$compressed_output" 2>/dev/null)
|
|
TOTAL_COMPRESSED_SIZE=$((TOTAL_COMPRESSED_SIZE + COMPRESSED_SIZE))
|
|
|
|
# Verify alpha preservation if original had alpha
|
|
if [ "$HAS_ALPHA_CONTENT" = true ]; then
|
|
OUTPUT_PIXEL_FORMAT=$(ffprobe -v quiet -select_streams v:0 -show_entries stream=pix_fmt -of csv=p=0 "$compressed_output" 2>/dev/null)
|
|
OUTPUT_HAS_ALPHA=$(echo "$OUTPUT_PIXEL_FORMAT" | grep -iE "(yuva|rgba|gbra|argb|abgr|bgra|a444|a422|a420)")
|
|
OUTPUT_ALPHA_MODE=$(ffprobe -v quiet -select_streams v:0 -show_entries stream_tags=alpha_mode -of csv=p=0 "$compressed_output" 2>/dev/null)
|
|
|
|
if [ -n "$OUTPUT_HAS_ALPHA" ] || [ "$OUTPUT_ALPHA_MODE" = "1" ]; then
|
|
echo " ✓ Alpha channel preserved (format: $OUTPUT_PIXEL_FORMAT, alpha_mode: $OUTPUT_ALPHA_MODE)"
|
|
else
|
|
echo " ⚠ Warning: Alpha channel may have been lost (output format: $OUTPUT_PIXEL_FORMAT)"
|
|
fi
|
|
fi
|
|
|
|
# Calculate compression ratio
|
|
RATIO=$(echo "scale=1; $COMPRESSED_SIZE * 100 / $ORIGINAL_SIZE" | bc)
|
|
SAVINGS=$(echo "scale=1; 100 - $RATIO" | bc)
|
|
|
|
if [ "$FILE_EXISTS" = true ]; then
|
|
echo " ✓ Successfully overwritten: ${base_name}.webm"
|
|
((OVERWRITTEN++))
|
|
else
|
|
echo " ✓ Successfully created: ${base_name}.webm"
|
|
((NEW_FILES++))
|
|
fi
|
|
echo " → Size: $(format_size $ORIGINAL_SIZE) → $(format_size $COMPRESSED_SIZE) (${RATIO}% of original, ${SAVINGS}% savings)"
|
|
((PROCESSED++))
|
|
else
|
|
echo " ✗ Failed to compress: $(basename "$webm_file")"
|
|
((FAILED++))
|
|
fi
|
|
echo ""
|
|
done
|
|
|
|
# Calculate overall compression statistics
|
|
if [ $TOTAL_ORIGINAL_SIZE -gt 0 ]; then
|
|
OVERALL_RATIO=$(echo "scale=1; $TOTAL_COMPRESSED_SIZE * 100 / $TOTAL_ORIGINAL_SIZE" | bc)
|
|
OVERALL_SAVINGS=$(echo "scale=1; 100 - $OVERALL_RATIO" | bc)
|
|
fi
|
|
|
|
# Summary
|
|
echo "=== Compression Complete ==="
|
|
echo "Total files processed: $PROCESSED"
|
|
if [ $NEW_FILES -gt 0 ]; then
|
|
echo "New files created: $NEW_FILES"
|
|
fi
|
|
if [ $OVERWRITTEN -gt 0 ]; then
|
|
echo "Existing files overwritten: $OVERWRITTEN"
|
|
fi
|
|
if [ $FAILED -gt 0 ]; then
|
|
echo "Failed to process: $FAILED files"
|
|
fi
|
|
echo "Output directory: $COMPRESSED_DIR"
|
|
echo "Settings used - Compression: Level $COMPRESSION ($COMP_DESC), Audio: $AUDIO_DESC, FPS: $FPS"
|
|
echo "Note: Alpha-containing files may use different compression settings for transparency preservation"
|
|
|
|
if [ $PROCESSED -gt 0 ]; then
|
|
echo ""
|
|
echo "=== Compression Statistics ==="
|
|
echo "Total original size: $(format_size $TOTAL_ORIGINAL_SIZE)"
|
|
echo "Total compressed size: $(format_size $TOTAL_COMPRESSED_SIZE)"
|
|
echo "Overall compression: ${OVERALL_RATIO}% of original (${OVERALL_SAVINGS}% space saved)"
|
|
fi
|
|
echo ""
|
|
|
|
# List generated files
|
|
if [ $PROCESSED -gt 0 ]; then
|
|
echo "Compressed WebM files:"
|
|
ls -la "$COMPRESSED_DIR"/*.webm 2>/dev/null | while read -r line; do
|
|
echo " $line"
|
|
done
|
|
fi
|