强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

FFmpeg 多媒体处理教程 / 批量处理

批量处理

概述

批量处理(Batch Processing)是指对大量多媒体文件进行自动化处理的技术。本章介绍如何使用 FFmpeg 进行高效的批量处理,包括脚本编写、并行处理、进度监控和错误处理。

基础脚本

简单批量转码

#!/bin/bash
# simple_batch.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    echo "处理: $filename"
    ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output"
done

echo "批量处理完成"

带进度显示的批量脚本

#!/bin/bash
# batch_with_progress.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}

mkdir -p "$OUTPUT_DIR"

# 统计文件数量
total=$(find "$INPUT_DIR" -name "*.mp4" | wc -l)
current=0

for file in "$INPUT_DIR"/*.mp4; do
    current=$((current + 1))
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    echo "[$current/$total] 处理: $filename"
    ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null
    
    if [ $? -eq 0 ]; then
        echo "  ✓ 成功"
    else
        echo "  ✗ 失败"
    fi
done

echo "批量处理完成"

格式转换批量脚本

#!/bin/bash
# batch_convert.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}
INPUT_FORMAT=${3:-avi}
OUTPUT_FORMAT=${4:-mp4}

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*."$INPUT_FORMAT"; do
    filename=$(basename "$file" ".$INPUT_FORMAT")
    output="$OUTPUT_DIR/${filename}.${OUTPUT_FORMAT}"
    
    echo "转换: $filename"
    ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output"
done

并行处理

使用 GNU Parallel

#!/bin/bash
# parallel_batch.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}
MAX_JOBS=${3:-4}

mkdir -p "$OUTPUT_DIR"

# 定义处理函数
process_file() {
    local file=$1
    local filename=$(basename "$file")
    local output="$OUTPUT_DIR/$filename"
    
    ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null
    
    if [ $? -eq 0 ]; then
        echo "✓ $filename"
    else
        echo "✗ $filename"
    fi
}

export -f process_file
export OUTPUT_DIR

# 使用 GNU Parallel 并行处理
find "$INPUT_DIR" -name "*.mp4" | parallel -j "$MAX_JOBS" process_file

echo "并行处理完成"

使用 xargs 并行

#!/bin/bash
# xargs_parallel.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}
MAX_JOBS=${3:-4}

mkdir -p "$OUTPUT_DIR"

process_file() {
    local file=$1
    local filename=$(basename "$file")
    local output="$OUTPUT_DIR/$filename"
    
    ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null
    echo "完成: $filename"
}

export -f process_file
export OUTPUT_DIR

find "$INPUT_DIR" -name "*.mp4" -print0 | xargs -0 -P "$MAX_JOBS" -I {} bash -c 'process_file "$@"' _ {}

使用后台进程

#!/bin/bash
# background_jobs.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}
MAX_JOBS=${3:-4}

mkdir -p "$OUTPUT_DIR"

job_count=0

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    # 后台处理
    (
        ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null
        echo "完成: $filename"
    ) &
    
    job_count=$((job_count + 1))
    
    # 限制并发数
    if [ $job_count -ge $MAX_JOBS ]; then
        wait -n
        job_count=$((job_count - 1))
    fi
done

# 等待所有任务完成
wait

echo "并行处理完成"

进度监控

使用 FFmpeg 进度输出

#!/bin/bash
# progress_monitor.sh

INPUT=$1
OUTPUT=$2

# 获取视频时长
duration=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$INPUT")

ffmpeg -y -i "$INPUT" -c:v libx264 -crf 23 -c:a aac "$OUTPUT" 2>&1 | while read line; do
    if echo "$line" | grep -q "time="; then
        # 提取当前时间
        current_time=$(echo "$line" | grep -oP 'time=\K[0-9:\.]+')
        
        # 转换为秒
        current_seconds=$(echo "$current_time" | awk -F: '{print $1*3600+$2*60+$3}')
        
        # 计算进度百分比
        progress=$(echo "scale=2; $current_seconds / $duration * 100" | bc)
        
        echo -ne "\r进度: ${progress}%"
    fi
done

echo ""
echo "处理完成"

使用 tqdm 进度条

#!/bin/bash
# tqdm_progress.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}

mkdir -p "$OUTPUT_DIR"

# 安装 tqdm(如果未安装)
# pip install tqdm

# 创建文件列表
find "$INPUT_DIR" -name "*.mp4" > /tmp/file_list.txt

# 使用 tqdm 显示进度
cat /tmp/file_list.txt | while read file; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null
    echo "$filename" >> /tmp/processed.txt
done | tqdm --total=$(wc -l < /tmp/file_list.txt) --unit=文件

rm -f /tmp/file_list.txt /tmp/processed.txt

日志记录

#!/bin/bash
# logging_batch.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}
LOG_FILE=${3:-batch.log}

mkdir -p "$OUTPUT_DIR"

# 初始化日志
echo "批量处理开始: $(date)" > "$LOG_FILE"
echo "输入目录: $INPUT_DIR" >> "$LOG_FILE"
echo "输出目录: $OUTPUT_DIR" >> "$LOG_FILE"
echo "---" >> "$LOG_FILE"

success_count=0
fail_count=0

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    echo "处理: $filename" >> "$LOG_FILE"
    
    if ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>>"$LOG_FILE"; then
        echo "  ✓ 成功" >> "$LOG_FILE"
        success_count=$((success_count + 1))
    else
        echo "  ✗ 失败" >> "$LOG_FILE"
        fail_count=$((fail_count + 1))
    fi
done

echo "---" >> "$LOG_FILE"
echo "批量处理完成: $(date)" >> "$LOG_FILE"
echo "成功: $success_count" >> "$LOG_FILE"
echo "失败: $fail_count" >> "$LOG_FILE"

echo "日志文件: $LOG_FILE"

错误处理

基本错误处理

#!/bin/bash
# error_handling.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    # 检查输入文件是否存在
    if [ ! -f "$file" ]; then
        echo "错误: 文件不存在 - $filename"
        continue
    fi
    
    # 检查文件大小
    if [ ! -s "$file" ]; then
        echo "错误: 文件为空 - $filename"
        continue
    fi
    
    # 处理文件
    if ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null; then
        echo "✓ $filename"
    else
        echo "✗ $filename"
    fi
done

重试机制

#!/bin/bash
# retry_mechanism.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}
MAX_RETRIES=${3:-3}

mkdir -p "$OUTPUT_DIR"

process_with_retry() {
    local file=$1
    local output=$2
    local retries=0
    
    while [ $retries -lt $MAX_RETRIES ]; do
        if ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null; then
            return 0
        fi
        
        retries=$((retries + 1))
        echo "  重试 $retries/$MAX_RETRIES"
        sleep 1
    done
    
    return 1
}

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    echo "处理: $filename"
    
    if process_with_retry "$file" "$output"; then
        echo "  ✓ 成功"
    else
        echo "  ✗ 失败(已重试 $MAX_RETRIES 次)"
    fi
done

超时处理

#!/bin/bash
# timeout_handling.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}
TIMEOUT=${3:-300}  # 5 分钟超时

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    echo "处理: $filename"
    
    # 使用 timeout 命令
    if timeout "$TIMEOUT" ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null; then
        echo "  ✓ 成功"
    else
        exit_code=$?
        if [ $exit_code -eq 124 ]; then
            echo "  ✗ 超时(${TIMEOUT}秒)"
            rm -f "$output"  # 删除不完整的输出
        else
            echo "  ✗ 失败"
        fi
    fi
done

断点续传

#!/bin/bash
# resume_batch.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}
CHECKPOINT_FILE=${3:-checkpoint.txt}

mkdir -p "$OUTPUT_DIR"

# 加载检查点
if [ -f "$CHECKPOINT_FILE" ]; then
    echo "加载检查点: $CHECKPOINT_FILE"
    processed_files=$(cat "$CHECKPOINT_FILE")
else
    processed_files=""
fi

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    # 检查是否已处理
    if echo "$processed_files" | grep -q "$filename"; then
        echo "跳过: $filename(已处理)"
        continue
    fi
    
    echo "处理: $filename"
    
    if ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null; then
        echo "  ✓ 成功"
        echo "$filename" >> "$CHECKPOINT_FILE"
    else
        echo "  ✗ 失败"
    fi
done

echo "批量处理完成"

高级批量处理

多格式输出

#!/bin/bash
# multi_format_output.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}

mkdir -p "$OUTPUT_DIR/mp4" "$OUTPUT_DIR/webm" "$OUTPUT_DIR/avi"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file" .mp4)
    
    echo "处理: $filename"
    
    # MP4 输出
    ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$OUTPUT_DIR/mp4/${filename}.mp4" 2>/dev/null
    
    # WebM 输出
    ffmpeg -y -i "$file" -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus "$OUTPUT_DIR/webm/${filename}.webm" 2>/dev/null
    
    # AVI 输出
    ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a libmp3lame "$OUTPUT_DIR/avi/${filename}.avi" 2>/dev/null
done

多分辨率输出

#!/bin/bash
# multi_resolution.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}

mkdir -p "$OUTPUT_DIR/1080p" "$OUTPUT_DIR/720p" "$OUTPUT_DIR/480p"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    
    echo "处理: $filename"
    
    # 1080p
    ffmpeg -y -i "$file" -vf scale=1920:1080 -c:v libx264 -crf 23 -c:a aac "$OUTPUT_DIR/1080p/$filename" 2>/dev/null
    
    # 720p
    ffmpeg -y -i "$file" -vf scale=1280:720 -c:v libx264 -crf 23 -c:a aac "$OUTPUT_DIR/720p/$filename" 2>/dev/null
    
    # 480p
    ffmpeg -y -i "$file" -vf scale=854:480 -c:v libx264 -crf 23 -c:a aac "$OUTPUT_DIR/480p/$filename" 2>/dev/null
done

带滤镜的批量处理

#!/bin/bash
# batch_with_filter.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}
FILTER=${3:-"scale=1280:720"}

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    echo "处理: $filename"
    ffmpeg -y -i "$file" -vf "$FILTER" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null
done

批量添加水印

#!/bin/bash
# batch_watermark.sh

INPUT_DIR=${1:-.}
WATERMARK=${2:-watermark.png}
OUTPUT_DIR=${3:-output}
POSITION=${4:-"W-w-10:H-h-10"}  # 右下角

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    echo "添加水印: $filename"
    ffmpeg -y -i "$file" -i "$WATERMARK" \
        -filter_complex "[0:v][1:v]overlay=$POSITION[v]" \
        -map "[v]" -map 0:a \
        -c:v libx264 -crf 23 -c:a copy \
        "$output" 2>/dev/null
done

批量提取音频

#!/bin/bash
# batch_extract_audio.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-audio}
FORMAT=${3:-mp3}

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file" .mp4)
    output="$OUTPUT_DIR/${filename}.${FORMAT}"
    
    echo "提取音频: $filename"
    
    case $FORMAT in
        mp3)
            ffmpeg -y -i "$file" -vn -c:a libmp3lame -q:a 2 "$output" 2>/dev/null
            ;;
        aac)
            ffmpeg -y -i "$file" -vn -c:a aac -b:a 192k "$output" 2>/dev/null
            ;;
        wav)
            ffmpeg -y -i "$file" -vn -c:a pcm_s16le "$output" 2>/dev/null
            ;;
        flac)
            ffmpeg -y -i "$file" -vn -c:a flac "$output" 2>/dev/null
            ;;
    esac
done

批量提取缩略图

#!/bin/bash
# batch_thumbnail.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-thumbnails}
INTERVAL=${3:-10}  # 每 10 秒提取一帧

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file" .mp4)
    mkdir -p "$OUTPUT_DIR/$filename"
    
    echo "提取缩略图: $filename"
    ffmpeg -y -i "$file" -vf "fps=1/$INTERVAL" \
        "$OUTPUT_DIR/$filename/thumb_%04d.jpg" 2>/dev/null
done

批量视频拼接

#!/bin/bash
# batch_concat.sh

INPUT_DIR=${1:-.}
OUTPUT=${2:-output.mp4}

# 创建文件列表
cat > /tmp/concat_list.txt << EOF
EOF

for file in "$INPUT_DIR"/*.mp4; do
    echo "file '$(realpath "$file")'" >> /tmp/concat_list.txt
done

echo "拼接视频..."
ffmpeg -y -f concat -safe 0 -i /tmp/concat_list.txt -c copy "$OUTPUT"

rm -f /tmp/concat_list.txt
echo "拼接完成: $OUTPUT"

配置文件驱动

使用配置文件

#!/bin/bash
# config_driven_batch.sh

CONFIG_FILE=${1:-config.json}

# 读取配置
INPUT_DIR=$(jq -r '.input_dir' "$CONFIG_FILE")
OUTPUT_DIR=$(jq -r '.output_dir' "$CONFIG_FILE")
CODEC=$(jq -r '.codec' "$CONFIG_FILE")
CRF=$(jq -r '.crf' "$CONFIG_FILE")
PRESET=$(jq -r '.preset' "$CONFIG_FILE")

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    echo "处理: $filename"
    ffmpeg -y -i "$file" -c:v "$CODEC" -crf "$CRF" -preset "$PRESET" -c:a aac "$output"
done

配置文件示例(config.json):

{
    "input_dir": "./input",
    "output_dir": "./output",
    "codec": "libx264",
    "crf": 23,
    "preset": "medium",
    "audio_codec": "aac",
    "audio_bitrate": "128k"
}

YAML 配置

# config.yaml
input_dir: ./input
output_dir: ./output
video:
  codec: libx264
  crf: 23
  preset: medium
  max_bitrate: 5M
audio:
  codec: aac
  bitrate: 128k
  sample_rate: 44100
#!/bin/bash
# yaml_config_batch.sh

CONFIG_FILE=${1:-config.yaml}

# 安装 yq(如果未安装)
# sudo apt install yq

INPUT_DIR=$(yq -r '.input_dir' "$CONFIG_FILE")
OUTPUT_DIR=$(yq -r '.output_dir' "$CONFIG_FILE")
CODEC=$(yq -r '.video.codec' "$CONFIG_FILE")
CRF=$(yq -r '.video.crf' "$CONFIG_FILE")
PRESET=$(yq -r '.video.preset' "$CONFIG_FILE")
AUDIO_CODEC=$(yq -r '.audio.codec' "$CONFIG_FILE")
AUDIO_BITRATE=$(yq -r '.audio.bitrate' "$CONFIG_FILE")

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    echo "处理: $filename"
    ffmpeg -y -i "$file" \
        -c:v "$CODEC" -crf "$CRF" -preset "$PRESET" \
        -c:a "$AUDIO_CODEC" -b:a "$AUDIO_BITRATE" \
        "$output"
done

监控与报告

生成处理报告

#!/bin/bash
# batch_with_report.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}
REPORT_FILE=${3:-report.html}

mkdir -p "$OUTPUT_DIR"

# 初始化报告
cat > "$REPORT_FILE" << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>批量处理报告</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        table { border-collapse: collapse; width: 100%; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #4CAF50; color: white; }
        tr:nth-child(even) { background-color: #f2f2f2; }
        .success { color: green; }
        .failed { color: red; }
    </style>
</head>
<body>
    <h1>批量处理报告</h1>
    <p>开始时间: $(date)</p>
    <table>
        <tr>
            <th>文件名</th>
            <th>原始大小</th>
            <th>输出大小</th>
            <th>压缩率</th>
            <th>状态</th>
        </tr>
EOF

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    original_size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
    
    if ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null; then
        output_size=$(stat -f%z "$output" 2>/dev/null || stat -c%s "$output" 2>/dev/null)
        compression=$(echo "scale=2; (1 - $output_size / $original_size) * 100" | bc)
        
        cat >> "$REPORT_FILE" << EOF
        <tr>
            <td>$filename</td>
            <td>$(numfmt --to=iec $original_size)</td>
            <td>$(numfmt --to=iec $output_size)</td>
            <td>${compression}%</td>
            <td class="success">✓ 成功</td>
        </tr>
EOF
    else
        cat >> "$REPORT_FILE" << EOF
        <tr>
            <td>$filename</td>
            <td>$(numfmt --to=iec $original_size)</td>
            <td>-</td>
            <td>-</td>
            <td class="failed">✗ 失败</td>
        </tr>
EOF
    fi
done

# 完成报告
cat >> "$REPORT_FILE" << 'EOF'
    </table>
    <p>结束时间: $(date)</p>
</body>
</html>
EOF

echo "报告生成: $REPORT_FILE"

统计信息

#!/bin/bash
# batch_statistics.sh

INPUT_DIR=${1:-.}
OUTPUT_DIR=${2:-output}

mkdir -p "$OUTPUT_DIR"

total_files=0
success_files=0
failed_files=0
total_input_size=0
total_output_size=0

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    total_files=$((total_files + 1))
    
    original_size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
    total_input_size=$((total_input_size + original_size))
    
    if ffmpeg -y -i "$file" -c:v libx264 -crf 23 -c:a aac "$output" 2>/dev/null; then
        success_files=$((success_files + 1))
        output_size=$(stat -f%z "$output" 2>/dev/null || stat -c%s "$output" 2>/dev/null)
        total_output_size=$((total_output_size + output_size))
    else
        failed_files=$((failed_files + 1))
    fi
done

echo "=== 批量处理统计 ==="
echo "总文件数: $total_files"
echo "成功: $success_files"
echo "失败: $failed_files"
echo "原始总大小: $(numfmt --to=iec $total_input_size)"
echo "输出总大小: $(numfmt --to=iec $total_output_size)"
echo "压缩率: $(echo "scale=2; (1 - $total_output_size / $total_input_size) * 100" | bc)%"

注意事项

  1. 资源管理:并行处理时注意 CPU 和内存使用
  2. 磁盘空间:确保有足够的磁盘空间存储输出文件
  3. 错误处理:完善的错误处理可以避免处理中断
  4. 日志记录:记录处理日志便于问题排查
  5. 断点续传:大批量处理时使用断点续传功能

业务场景

场景 1:视频网站批量转码

#!/bin/bash
# video_site_transcode.sh

INPUT_DIR=$1
OUTPUT_DIR=$2
MAX_JOBS=${3:-8}

mkdir -p "$OUTPUT_DIR/1080p" "$OUTPUT_DIR/720p" "$OUTPUT_DIR/480p"

transcode() {
    local file=$1
    local filename=$(basename "$file" .mp4)
    
    # 1080p
    ffmpeg -y -i "$file" -vf scale=1920:1080 \
        -c:v libx264 -preset fast -crf 23 \
        -c:a aac -b:a 192k \
        "$OUTPUT_DIR/1080p/${filename}.mp4" 2>/dev/null
    
    # 720p
    ffmpeg -y -i "$file" -vf scale=1280:720 \
        -c:v libx264 -preset fast -crf 23 \
        -c:a aac -b:a 128k \
        "$OUTPUT_DIR/720p/${filename}.mp4" 2>/dev/null
    
    # 480p
    ffmpeg -y -i "$file" -vf scale=854:480 \
        -c:v libx264 -preset fast -crf 23 \
        -c:a aac -b:a 96k \
        "$OUTPUT_DIR/480p/${filename}.mp4" 2>/dev/null
    
    echo "完成: $filename"
}

export -f transcode
export OUTPUT_DIR

find "$INPUT_DIR" -name "*.mp4" | parallel -j "$MAX_JOBS" transcode

场景 2:监控录像批量处理

#!/bin/bash
# surveillance_batch.sh

INPUT_DIR=$1
OUTPUT_DIR=$2

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    # 压缩并添加时间戳
    ffmpeg -y -i "$file" \
        -vf "scale=640:360,drawtext=text='%{localtime\:%Y-%m-%d %H\\\:%M\\\:%S}':x=10:y=10:fontsize=16:fontcolor=white" \
        -c:v libx264 -preset fast -crf 28 \
        -an \
        "$output" 2>/dev/null
done

场景 3:社交媒体批量发布

#!/bin/bash
# social_media_batch.sh

INPUT_DIR=$1
OUTPUT_DIR=$2

mkdir -p "$OUTPUT_DIR/instagram" "$OUTPUT_DIR/tiktok" "$OUTPUT_DIR/youtube"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file" .mp4)
    
    # Instagram (1:1)
    ffmpeg -y -i "$file" \
        -vf "scale=1080:1080:force_original_aspect_ratio=decrease,pad=1080:1080:(ow-iw)/2:(oh-ih)/2" \
        -c:v libx264 -crf 23 -c:a aac \
        "$OUTPUT_DIR/instagram/${filename}.mp4" 2>/dev/null
    
    # TikTok (9:16)
    ffmpeg -y -i "$file" \
        -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" \
        -c:v libx264 -crf 23 -c:a aac \
        "$OUTPUT_DIR/tiktok/${filename}.mp4" 2>/dev/null
    
    # YouTube (16:9)
    ffmpeg -y -i "$file" \
        -vf scale=1920:1080 \
        -c:v libx264 -crf 23 -c:a aac \
        "$OUTPUT_DIR/youtube/${filename}.mp4" 2>/dev/null
done

扩展阅读

  1. GNU Parallel 文档
  2. Bash 脚本指南
  3. FFmpeg 批量处理
  4. FFmpeg 进度输出

总结

本章介绍了 FFmpeg 的批量处理技术,包括:

  • 基础批量脚本
  • 并行处理方法
  • 进度监控技巧
  • 错误处理机制
  • 高级批量处理功能

掌握批量处理技术可以大幅提高视频处理效率,特别适合需要处理大量视频的场景。