Created
July 4, 2025 09:13
-
-
Save raylax/1c06fb7af7e9eefa854436dea395a404 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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