Inital push
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Extracts a specific frame or the last frame of a video file to a PNG image using ffmpeg.
|
||||
.PARAMETER Video
|
||||
Path to the input video file. Can be absolute or relative to current directory.
|
||||
.PARAMETER Image
|
||||
Path to the output PNG image file. Can be absolute or relative to current directory.
|
||||
.PARAMETER Frame
|
||||
0-based frame index to extract. Negative values count from the end:
|
||||
-1 extracts the last frame (default), -2 extracts second-to-last, etc.
|
||||
The resulting frame index is clamped to the valid range (0 to total frames - 1).
|
||||
.DESCRIPTION
|
||||
Extracts a specific frame of a video file to a PNG image using ffmpeg.
|
||||
Supports negative frame indices to count from the end of the video.
|
||||
Supports -WhatIf and -Confirm for safe execution.
|
||||
#>
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Video,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Image,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[int]$Frame = -1
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$VideoPath = $Video
|
||||
|
||||
if (-not (Test-Path $VideoPath)) {
|
||||
Write-Error ('Video file not found: {0}' -f $VideoPath)
|
||||
exit 1
|
||||
}
|
||||
|
||||
$PSNativeCommandUseErrorActionPreference = $false
|
||||
|
||||
$totalFrames = & ffprobe -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames -of csv=p=0 $VideoPath 2>&1 | Select-Object -First 1
|
||||
|
||||
if (-not $totalFrames -or $totalFrames -notmatch '^\d+$') {
|
||||
Write-Error ('Could not determine frame count for: {0}' -f $VideoPath)
|
||||
exit 1
|
||||
}
|
||||
|
||||
$totalFramesInt = [int]$totalFrames
|
||||
$maxFrameIndex = $totalFramesInt - 1
|
||||
|
||||
$frameToExtract = if ($Frame -lt 0) {
|
||||
# Negative values count from the end
|
||||
$calculatedFrame = $totalFramesInt + $Frame
|
||||
# Clamp to valid range [0, maxFrameIndex]
|
||||
[Math]::Max(0, [Math]::Min($calculatedFrame, $maxFrameIndex))
|
||||
} else {
|
||||
# Positive values are direct indices
|
||||
if ($Frame -gt $maxFrameIndex) {
|
||||
Write-Error ('Frame index {0} is out of range. Valid range: 0 to {1}' -f $Frame, $maxFrameIndex)
|
||||
exit 1
|
||||
}
|
||||
$Frame
|
||||
}
|
||||
|
||||
Write-Host ('Total frames: {0} - extracting frame index {1}' -f $totalFramesInt, $frameToExtract)
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($Image, ('Extract frame {0} to PNG' -f $frameToExtract))) {
|
||||
$videoFilter = 'select=eq(n\,{0})' -f $frameToExtract
|
||||
$ffmpegStderr = & ffmpeg -y -i $VideoPath -vf $videoFilter -vsync vfr -frames:v 1 -q:v 1 $Image 2>&1
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
$ffmpegStderr | ForEach-Object { Write-Error $_ }
|
||||
Write-Error 'ffmpeg failed to extract the last frame.'
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ('Frame saved to: {0}' -f $Image)
|
||||
}
|
||||
Reference in New Issue
Block a user