Skip to content

Instantly share code, notes, and snippets.

@arenagroove
Last active June 13, 2025 03:56
Show Gist options
  • Select an option

  • Save arenagroove/691e583284132b079aaa7cecf66702a0 to your computer and use it in GitHub Desktop.

Select an option

Save arenagroove/691e583284132b079aaa7cecf66702a0 to your computer and use it in GitHub Desktop.
PowerShell script to generate scrub-optimized video files with FFmpeg. Includes poster frames (JPG, PNG, WebP) and a metadata report via ffprobe. Outputs are grouped in timestamped folders.

Scrub-Optimized Video Encoder (PowerShell)

This PowerShell script uses FFmpeg to produce a .mp4 video optimized for timeline scrubbing (frequent keyframes, fast-start metadata).
It also extracts poster frames and generates a plain-text metadata report.


✅ Features

  • Constant keyframe interval (-g, -keyint_min) for frame-accurate scrubbing
  • Output scaled to fixed height (default: 1080px) while maintaining aspect ratio
  • Poster images extracted at 00:00:01 as:
    • .jpg
    • .png
    • .webp
  • FFprobe metadata report saved as .txt
  • All outputs are grouped inside a folder named:
    {input}_scrub_{timestamp}
    

⚙️ Requirements

  • ffmpeg and ffprobe must be available in your system’s PATH

🚀 Usage

.\Encode-ScrubVideo.ps1 -InputFile "your_video.mp4"

Optional Parameters

Parameter Description Default
-MaxHeight Target video height (width is auto-scaled) 1080
-KeyframeInterval Keyframe interval in frames 6
-CRF Constant Rate Factor (quality, 0–51) 20
-Preset Encoding speed/efficiency tradeoff slow
-Tune FFmpeg tuning hint (e.g. animation, film) animation

Example with all options:

.\Encode-ScrubVideo.ps1 -InputFile "forest.mp4" -CRF 18 -Preset veryslow -Tune animation

🗂 Output Structure

{input}scrub{timestamp}/
├── {input}scrub{timestamp}.mp4
├── {input}poster{timestamp}.jpg
├── {input}poster{timestamp}.png
├── {input}poster{timestamp}.webp
└── {input}metadata{timestamp}.txt

🔗 Demo

Used for video timeline scrubbing in this demos:
https://codepen.io/luis-lessrain/pen/zxGjErP
[https://codepen.io/luis-lessrain/pen/qEdWYrx

<#
.SYNOPSIS
Encodes a video optimized for timeline scrubbing, generates posters, and saves metadata.
.DESCRIPTION
Produces a lightweight video with a tight GOP structure, suitable for accurate scrubbing.
Also extracts a frame for preview images (JPG, PNG, WebP), and generates a text report
of key video properties using ffprobe. Allows control of FFmpeg quality and compression settings.
.PARAMETER InputFile
Input video file (.mp4, .mov, etc.)
.PARAMETER OutputFile
Optional. Output video filename. Defaults to {input}_scrub_{timestamp}.mp4
.PARAMETER MaxHeight
Scales video height (width is automatic). Default: 1080
.PARAMETER KeyframeInterval
Keyframe interval in frames. Lower = more accurate scrubbing. Default: 6
.PARAMETER CRF
Constant Rate Factor for FFmpeg (lower is higher quality). Range: 0–51. Default: 20
.PARAMETER Preset
Encoding speed/efficiency tradeoff. E.g., veryslow, slow, medium. Default: slow
.PARAMETER Tune
Content tuning hint. E.g., animation, film, stillimage. Optional.
#>
param (
[Parameter(Mandatory = $true)]
[string]$InputFile,
[string]$OutputFile = "",
[int]$MaxHeight = 1080,
[int]$KeyframeInterval = 6,
[ValidateRange(0, 51)]
[int]$CRF = 20,
[ValidateSet("ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow")]
[string]$Preset = "slow",
[string]$Tune = "animation"
)
# Timestamp
$timestamp = Get-Date -Format "yyyyMMdd_HHmm"
$inputBase = [System.IO.Path]::GetFileNameWithoutExtension($InputFile)
$outputName = "${inputBase}_scrub_${timestamp}.mp4"
$outputDir = "${inputBase}_scrub_${timestamp}"
# Create folder
New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
# Fallback if no output provided
if ([string]::IsNullOrWhiteSpace($OutputFile)) {
$OutputFile = Join-Path $outputDir $outputName
}
$posterBase = Join-Path $outputDir "${inputBase}_poster_${timestamp}"
$reportFile = Join-Path $outputDir "${inputBase}_metadata_${timestamp}.txt"
# ---------------------
# STEP 1: Encode Video
# ---------------------
$ffmpegArgs = @(
"-i", "`"$InputFile`"",
"-vf", "`"format=yuv420p,scale=-1:$MaxHeight`"",
"-c:v", "libx264",
"-profile:v", "main",
"-level:v", "5.1",
"-crf", "$CRF",
"-preset", "$Preset"
)
if (-not [string]::IsNullOrWhiteSpace($Tune)) {
$ffmpegArgs += @("-tune", "$Tune")
}
$ffmpegArgs += @(
"-movflags", "+faststart",
"-g", "$KeyframeInterval",
"-keyint_min", "$KeyframeInterval",
"-sc_threshold", "0",
"-an",
"`"$OutputFile`""
)
Write-Host "`n[Encode] Scrub-optimized video:"
Write-Host "ffmpeg $($ffmpegArgs -join ' ')`n"
ffmpeg @ffmpegArgs
# ---------------------
# STEP 2: Generate Posters
# ---------------------
$posterTime = "00:00:01"
$posterFormats = @("jpg", "png", "webp")
foreach ($ext in $posterFormats) {
$posterFile = "$posterBase.$ext"
$posterArgs = @(
"-i", "`"$InputFile`"",
"-ss", $posterTime,
"-vframes", "1",
"`"$posterFile`""
)
Write-Host "[Poster] Generating: $posterFile"
ffmpeg @posterArgs
}
# ---------------------
# STEP 3: Write Metadata Report
# ---------------------
$ffmpegCmdLine = "ffmpeg " + ($ffmpegArgs -join " ")
$ffprobeArgs = @(
"-v", "error",
"-select_streams", "v:0",
"-show_entries", "stream=codec_name,codec_type,bit_rate,profile,width,height,level,r_frame_rate,nb_frames,duration,avg_frame_rate",
"-of", "default=noprint_wrappers=1:nokey=0",
"`"$OutputFile`""
)
Write-Host "[Metadata] Generating report: $reportFile"
$reportHeader = "FFmpeg command:`n$ffmpegCmdLine`n`n--------------------------`nFFprobe Metadata:`n--------------------------`n"
$reportContent = $reportHeader + (& ffprobe @ffprobeArgs | Out-String)
$reportContent | Out-File -Encoding UTF8 $reportFile
# ---------------------
# Done
# ---------------------
Write-Host "`n✅ Done."
Write-Host " Encoded video : $OutputFile"
Write-Host " Poster images : $posterBase.jpg, $posterBase.png, $posterBase.webp"
Write-Host " Metadata file : $reportFile"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment