Fix -Frames negative range calculation to extract last N frames correctly (off-by-one error), add validation to reject invalid '-%' and '-0' formats with exit code 1, change '%' alone to extract single last frame instead of range, remove duplicate last frame from PNG sequence concat list, update Get-LastFrame.ps1 and Get-Frame.ps1 to clamp out-of-range frame indices instead of erroring and improve error messages with inline stderr output, add -y flag and -- separator to ffprobe calls, replace deprecated
This commit is contained in:
1 parent
cf41fb0ce5
commit
1227c6e153
13 files changed
+100
-86
No files matched your search
+18
-19
@@ -12,7 +12,8 @@
|
||||
.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).
|
||||
The resulting frame index is clamped to the valid range (0 to total frames - 1): a positive value past the last frame
|
||||
gives the last frame, and a negative value before the first frame gives frame 0.
|
||||
.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.
|
||||
@@ -36,14 +37,14 @@ $ErrorActionPreference = 'Stop'
|
||||
|
||||
$VideoPath = $Video
|
||||
|
||||
if (-not (Test-Path $VideoPath)) {
|
||||
if (-not (Test-Path -LiteralPath $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
|
||||
$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)
|
||||
@@ -51,21 +52,15 @@ if (-not $totalFrames -or $totalFrames -notmatch '^\d+$') {
|
||||
}
|
||||
|
||||
$totalFramesInt = [int]$totalFrames
|
||||
if ($totalFramesInt -lt 1) {
|
||||
Write-Error ('Video has no frames: {0}' -f $VideoPath)
|
||||
exit 1
|
||||
}
|
||||
$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
|
||||
}
|
||||
# Negative values count from the end; the result is clamped to [0, maxFrameIndex]
|
||||
$requestedFrame = if ($Frame -lt 0) { $totalFramesInt + $Frame } else { $Frame }
|
||||
$frameToExtract = [Math]::Max(0, [Math]::Min($requestedFrame, $maxFrameIndex))
|
||||
|
||||
Write-Host ('Total frames: {0} - extracting frame index {1}' -f $totalFramesInt, $frameToExtract)
|
||||
|
||||
@@ -98,7 +93,7 @@ if ($PSCmdlet.ShouldProcess($ImagePath, ('Extract frame {0} to PNG' -f $frameToE
|
||||
'-y'
|
||||
'-i', $VideoPath
|
||||
'-vf', $videoFilter
|
||||
'-vsync', 'vfr'
|
||||
'-fps_mode', 'vfr'
|
||||
'-frames:v', '1'
|
||||
'-q:v', '1'
|
||||
'--', $ImagePath
|
||||
@@ -107,8 +102,12 @@ if ($PSCmdlet.ShouldProcess($ImagePath, ('Extract frame {0} to PNG' -f $frameToE
|
||||
$ffmpegStderr = & ffmpeg @ffmpegArgs 2>&1
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
$ffmpegStderr | ForEach-Object { Write-Error $_ }
|
||||
Write-Error 'ffmpeg failed to extract the last frame.'
|
||||
Write-Error ('ffmpeg failed to extract frame {0}: {1}' -f $frameToExtract, (($ffmpegStderr | Out-String).Trim()))
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $ImagePath)) {
|
||||
Write-Error ('ffmpeg reported success but no image was written for frame {0}: {1}' -f $frameToExtract, $ImagePath)
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user