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:

  1. 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.
  2. 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"
done

Windows 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