Skip to content

Instantly share code, notes, and snippets.

@raylax
Created July 4, 2025 09:13
Show Gist options
  • Save raylax/1c06fb7af7e9eefa854436dea395a404 to your computer and use it in GitHub Desktop.
Save raylax/1c06fb7af7e9eefa854436dea395a404 to your computer and use it in GitHub Desktop.
#!/bin/bash
# 信号处理函数
cleanup() {
echo ""
echo "Interrupted by user."
exit 1
}
# 设置信号处理
trap cleanup SIGINT SIGTERM
# 解析命令行参数
while getopts "i:o:p:" opt; do
case $opt in
i) input="$OPTARG" ;;
o) output="$OPTARG" ;;
p) preset_arg="$OPTARG" ;;
*)
echo "Usage: $0 -i <input> -o <output> -p <preset_resolution>"
echo "Example: $0 -i video.mp4 -o output -p 720p"
echo "Example: $0 -i video.mp4 -o output -p 720p,1080p,480p"
echo "Preset resolutions: 480p, 720p, 1080p, 2k, 4k"
exit 1
;;
esac
done
if [ -z "$input" ]; then
echo "Error: Please specify input file with -i"
echo "Usage: $0 -i <input> -o <output> -p <preset_resolution>"
exit 1
fi
if [ -z "$output" ]; then
echo "Error: Please specify output directory with -o"
echo "Usage: $0 -i <input> -o <output> -p <preset_resolution>"
exit 1
fi
if [ -z "$preset_arg" ]; then
echo "Error: Please specify preset resolution with -p"
echo "Usage: $0 -i <input> -o <output> -p <preset_resolution>"
exit 1
fi
# 检查输入文件是否存在
if [ ! -f "$input" ]; then
echo "Error: Input file '$input' not found!"
exit 1
fi
# 将预设参数按逗号分割
IFS=',' read -ra preset_array <<< "$preset_arg"
resolutions=(
# SD
"480p 854x480 600 64"
# HD
"720p 1280x720 2000 128"
# FHD
"1080p 1920x1080 3800 160"
# QHD
"2k 2560x1440 5000 160"
# UHD
"4k 3840x2160 6000 160"
)
# 验证所有预设参数
for preset in "${preset_array[@]}"; do
preset_found=false
for resolution in "${resolutions[@]}"; do
preset_index=$(echo "$resolution" | awk '{print $1}')
if [ "$preset_index" == "$preset" ]; then
preset_found=true
break
fi
done
if [ "$preset_found" == false ]; then
echo "Error: Preset resolution not found: $preset"
echo "Available presets: 480p, 720p, 1080p, 2k, 4k"
exit 1
fi
done
max_fps=30
ac_num=2
# 读取视频码率(bps)
orig_vbr=$(ffprobe -v error -select_streams v:0 -show_entries stream=bit_rate \
-of default=noprint_wrappers=1:nokey=1 "$input")
# 读取音频码率(bps)
orig_abr=$(ffprobe -v error -select_streams a:0 -show_entries stream=bit_rate \
-of default=noprint_wrappers=1:nokey=1 "$input")
# 读取视频分辨率
orig_width=$(ffprobe -v error -select_streams v:0 -show_entries stream=width \
-of default=noprint_wrappers=1:nokey=1 "$input")
# 读取视频分辨率
orig_height=$(ffprobe -v error -select_streams v:0 -show_entries stream=height \
-of default=noprint_wrappers=1:nokey=1 "$input")
# 计算视频宽高比
if is_valid_number "$orig_width" && is_valid_number "$orig_height"; then
orig_aspect_ratio=$(echo "$orig_width / $orig_height" | bc -l)
else
orig_aspect_ratio=1.777777 # 默认16:9
fi
# 读取视频帧率
orig_fps=$(ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate \
-of default=noprint_wrappers=1:nokey=1 "$input")
orig_fps_val=$(echo "scale=2; $orig_fps" | bc) # 转成小数
# 视频帧率判断
if [[ -z "$orig_fps" ]]; then
fps=$max_fps
elif (( $(echo "$orig_fps_val < $max_fps" | bc -l) )); then
fps=$(printf "%.0f" "$orig_fps_val")
else
fps=$max_fps
fi
# 视频码率判断
if [[ -z "$orig_vbr" ]] || [[ "$orig_vbr" == "N/A" ]]; then
vbr=$max_vbr
elif [[ "$orig_vbr" -gt "$max_vbr" ]]; then
vbr=$max_vbr
else
vbr=$orig_vbr
fi
# 音频码率判断
if [[ -z "$orig_abr" ]] || [[ "$orig_abr" == "N/A" ]]; then
abr=$max_abr
elif [[ "$orig_abr" -gt "$max_abr" ]]; then
abr=$max_abr
else
abr=$orig_abr
fi
function format_kbps() {
local value=$1
if is_valid_number "$value"; then
echo "$value" | awk '{printf "%.0f\n", $1/1000}'
else
echo "0"
fi
}
function format_aspect_ratio() {
local ratio=$1
# 常见的宽高比
if (( $(echo "$ratio > 1.7" | bc -l) )); then
echo "16:9"
elif (( $(echo "$ratio > 1.3" | bc -l) )); then
echo "4:3"
elif (( $(echo "$ratio > 1.1" | bc -l) )); then
echo "3:2"
elif (( $(echo "$ratio > 0.9" | bc -l) )); then
echo "1:1"
elif (( $(echo "$ratio > 0.7" | bc -l) )); then
echo "3:4"
elif (( $(echo "$ratio > 0.5" | bc -l) )); then
echo "9:16"
else
echo "$(printf "%.2f" $ratio):1"
fi
}
function is_valid_number() {
local num=$1
[[ -n "$num" && "$num" != "N/A" && "$num" -gt 0 ]] 2>/dev/null
}
echo "Input: $input"
echo "Output: $output"
echo "Presets: ${preset_array[*]}"
echo "Original: ${orig_width}x${orig_height} (aspect ratio: $(format_aspect_ratio $orig_aspect_ratio))"
echo "FPS: ${fps}fps (max: ${max_fps}fps)"
# 为每个预设分辨率进行转码
for preset in "${preset_array[@]}"; do
echo ""
echo "=== Processing $preset ==="
# 查找对应的分辨率配置
for resolution in "${resolutions[@]}"; do
preset_index=$(echo "$resolution" | awk '{print $1}')
if [ "$preset_index" == "$preset" ]; then
max_vbr=$(echo "$resolution" | awk '{print $3}')
max_abr=$(echo "$resolution" | awk '{print $4}')
max_scale=$(echo "$resolution" | awk '{print $2}')
max_vbr=$(echo "$max_vbr * 1000" | bc)
max_abr=$(echo "$max_abr * 1000" | bc)
break
fi
done
# 计算最大分辨率
max_width=$(echo "$max_scale" | cut -d'x' -f1)
max_height=$(echo "$max_scale" | cut -d'x' -f2)
# 保持宽高比的缩放计算
if is_valid_number "$orig_width" && is_valid_number "$orig_height" && is_valid_number "$max_width" && is_valid_number "$max_height"; then
if (( $(echo "$orig_width / $orig_height > $max_width / $max_height" | bc -l) )); then
# 原视频更宽,以宽度为基准缩放
target_width=$max_width
target_height=$(echo "$orig_height * $max_width / $orig_width" | bc)
else
# 原视频更高,以高度为基准缩放
target_height=$max_height
target_width=$(echo "$orig_width * $max_height / $orig_height" | bc)
fi
else
# 使用默认值
target_width=$max_width
target_height=$max_height
fi
# 确保分辨率是偶数(H.264要求)
target_width=$((target_width / 2 * 2))
target_height=$((target_height / 2 * 2))
# 视频码率判断
if [[ -z "$orig_vbr" ]] || [[ "$orig_vbr" == "N/A" ]]; then
vbr=$max_vbr
elif [[ "$orig_vbr" -gt "$max_vbr" ]]; then
vbr=$max_vbr
else
vbr=$orig_vbr
fi
# 音频码率判断
if [[ -z "$orig_abr" ]] || [[ "$orig_abr" == "N/A" ]]; then
abr=$max_abr
elif [[ "$orig_abr" -gt "$max_abr" ]]; then
abr=$max_abr
else
abr=$orig_abr
fi
echo "Target: ${target_width}x${target_height} (max: ${max_scale})"
echo "VBR: $(format_kbps $vbr)kbps (max: $(format_kbps $max_vbr)kbps)"
echo "ABR: $(format_kbps $abr)kbps (max: $(format_kbps $max_abr)kbps)"
output_dir="$output/${preset_index}"
# 创建输出目录
mkdir -p "$output_dir"
# 转码
ffmpeg -y -i "$input" \
-map_metadata -1 \
-c:v libx264 -b:v $vbr -maxrate $vbr -bufsize $((vbr * 2)) \
-vf scale=$target_width:$target_height \
-preset veryfast -r $fps -g $fps \
-c:a aac -b:a $abr \
-hls_time 10 -hls_list_size 0 \
-hls_segment_filename "$output_dir/%03d.ts" \
-f hls "$output_dir/playlist.m3u8"
# 检查FFmpeg是否成功
if [ $? -ne 0 ]; then
echo "Error: FFmpeg failed for $preset"
exit 1
fi
echo "Completed: $preset -> $output_dir/"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment