Two-Pass AV1 Encoding with Batch Scripts
This article provides a technical guide on automating two-pass AV1 video encoding across multiple media files using standard command-line batch processing scripts. Implementing a two-pass workflow enables optimal rate control and compression efficiency, but requires structured orchestration to generate intermediate analysis logs, apply them during the final encode, and clean up temporary data sequentially. Below are the precise steps, command structures, and scripts needed to build an automated encoding pipeline using FFmpeg and modern AV1 encoders like SVT-AV1.
Two-Pass Encoding Mechanics
Two-pass encoding splits video compression into two distinct phases:
- Pass 1 (Analysis): The encoder analyzes the entire
video file to determine scene complexity, motion vectors, and frame
requirements. It outputs a temporary statistics log (often named
ffmpeg2pass-0.log) rather than a usable video file. - Pass 2 (Execution): The encoder reads the statistics log to allocate bitrates dynamically, giving high bitrates to complex scenes and conserving bits during static scenes, producing the final output container.
To maximize efficiency during batch operations, the first pass should
discard audio (-an) and route the video to a null sink
(-f null /dev/null on Unix or -f null NUL on
Windows), drastically reducing processing time.
Linux/macOS Bash Orchestration
In a Unix environment, a standard Bash script can iterate through an input directory, execute both passes sequentially for each file, and manage logs to avoid file collision.
#!/usr/bin/env bash
set -euo pipefail
INPUT_DIR="./inputs"
OUTPUT_DIR="./outputs"
mkdir -p "$OUTPUT_DIR"
for input in "$INPUT_DIR"/*.mp4; do
[ -e "$input" ] || continue
filename=$(basename -- "$input")
basename="${filename%.*}"
passlog_prefix="${OUTPUT_DIR}/${basename}_passlog"
output_file="${OUTPUT_DIR}/${basename}_av1.mp4"
echo "Starting Pass 1 for: $filename"
ffmpeg -y -i "$input" \
-c:v libsvtav1 -b:v 2000k -preset 5 \
-pass 1 -passlogfile "$passlog_prefix" \
-an -f null /dev/null
echo "Starting Pass 2 for: $filename"
ffmpeg -y -i "$input" \
-c:v libsvtav1 -b:v 2000k -preset 5 \
-pass 2 -passlogfile "$passlog_prefix" \
-c:a libopus -b:a 128k \
"$output_file"
# Clean up pass log files
rm -f "${passlog_prefix}"*.log
echo "Completed: $output_file"
doneWindows PowerShell Orchestration
For Windows environments, PowerShell provides robust path-handling and error management for sequential two-pass jobs.
$InputDir = ".\inputs"
$OutputDir = ".\outputs"
if (!(Test-Path $OutputDir)) {
New-Item -ItemType Directory -Path $OutputDir | Out-Null
}
Get-ChildItem -Path $InputDir -Filter *.mp4 | ForEach-Object {
$baseName = $_.BaseName
$inputFile = $_.FullName
$logPrefix = Join-Path $OutputDir "$($baseName)_passlog"
$outputFile = Join-Path $OutputDir "$($baseName)_av1.mp4"
Write-Host "Running Pass 1 for: $($_.Name)" -ForegroundColor Cyan
& ffmpeg -y -i $inputFile `
-c:v libsvtav1 -b:v 2000k -preset 5 `
-pass 1 -passlogfile $logPrefix `
-an -f null NUL
Write-Host "Running Pass 2 for: $($_.Name)" -ForegroundColor Green
& ffmpeg -y -i $inputFile `
-c:v libsvtav1 -b:v 2000k -preset 5 `
-pass 2 -passlogfile $logPrefix `
-c:a libopus -b:a 128k `
$outputFile
# Clean up generated analysis files
Remove-Item -Path "$($logPrefix)*.log" -ErrorAction SilentlyContinue
}Key Considerations for Automated Pipelines
- Log Prefixing: Always define an explicit prefix via
-passlogfile. Without this, FFmpeg defaults to generic filenames in the current working directory, which can cause race conditions or corrupt analysis data if scripts run concurrently. - Consistent Encoding Parameters: Parameters that
alter frame structures—such as
-preset, framerate changes, or video filters—must remain identical between Pass 1 and Pass 2. Mismatched parameters degrade output quality and can cause the encoder to fail. - Failure Handling: Ensure scripts check return codes between passes. If Pass 1 aborts due to a corrupt source frame, Pass 2 must be skipped to avoid processing incomplete or corrupted logs.