Add exact-fit mode to Compare-Videos.ps1 for two sources that already tile the output frame perfectly (e.g. 1280x1080 + 640x1080 -> 1920x1080), stacking them side by side with no scaling or padding when both heights match output height and widths sum to output width, refactor dimension probing into Get-VideoDimensions function to eliminate duplicate ffprobe calls, and update documentation to explain exact-fit behavior and that -Clip and -Sideways have no effect in this mode
This commit is contained in:
1 parent
70b4b83be0
commit
ae7880af95
1 file changed
+71
-42
+71
-42
@@ -8,6 +8,12 @@
|
|||||||
Each source video is scaled to fit its cell without stretching, with black letterbox/pillarbox
|
Each source video is scaled to fit its cell without stretching, with black letterbox/pillarbox
|
||||||
padding applied as needed to preserve aspect ratios.
|
padding applied as needed to preserve aspect ratios.
|
||||||
|
|
||||||
|
Exact fit (2 videos only): when S1 and S2 are both exactly as tall as the output
|
||||||
|
(1080, or 2160 with -In4K) and their widths add up to the output width (1920, or 3840
|
||||||
|
with -In4K), the two videos already tile the target frame perfectly. In that case they
|
||||||
|
are stacked side by side untouched - no scaling, no padding, no quality loss.
|
||||||
|
Example: 1280x1080 + 640x1080 -> 1920x1080. -Clip and -Sideways have no effect here.
|
||||||
|
|
||||||
The script detects the orientation of S1 (landscape or portrait) and applies appropriate scaling:
|
The script detects the orientation of S1 (landscape or portrait) and applies appropriate scaling:
|
||||||
|
|
||||||
Landscape mode (width > height) - 1K HD:
|
Landscape mode (width > height) - 1K HD:
|
||||||
@@ -78,6 +84,9 @@
|
|||||||
Generate comparison video in 4K UHD (3840x2160) resolution instead of 1K HD (1920x1080).
|
Generate comparison video in 4K UHD (3840x2160) resolution instead of 1K HD (1920x1080).
|
||||||
|
|
||||||
.PARAMETER Clip
|
.PARAMETER Clip
|
||||||
|
Ignored when the two sources are an exact fit for the output frame (see DESCRIPTION),
|
||||||
|
since such an output has no padding to remove.
|
||||||
|
|
||||||
Reduces the output height to match the actual scaled height of the sources, removing
|
Reduces the output height to match the actual scaled height of the sources, removing
|
||||||
the top/bottom black letterbox padding. For landscape, only applies when exactly 2
|
the top/bottom black letterbox padding. For landscape, only applies when exactly 2
|
||||||
source videos are provided; ignored otherwise. For portrait, applies when 2-4 source
|
source videos are provided; ignored otherwise. For portrait, applies when 2-4 source
|
||||||
@@ -135,6 +144,12 @@
|
|||||||
|
|
||||||
Compare two 720p (1280x720) videos with no top/bottom letterboxing: output is 1920x540.
|
Compare two 720p (1280x720) videos with no top/bottom letterboxing: output is 1920x540.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Compare-Videos.ps1 -S1 wide_1280x1080.mp4 -S2 narrow_640x1080.mp4 -Output exact.mp4
|
||||||
|
|
||||||
|
Exact fit: both sources are 1080 tall and 1280 + 640 = 1920, so they are stacked
|
||||||
|
side by side with no scaling or padding at all.
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
.\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -Output sideways.mp4 -Sideways
|
.\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -Output sideways.mp4 -Sideways
|
||||||
|
|
||||||
@@ -202,6 +217,20 @@ function Test-VideoExists {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Get-VideoDimensions {
|
||||||
|
param([string]$Path, [string]$Name)
|
||||||
|
|
||||||
|
$probeOutput = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $Path 2>&1
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Failed to probe video dimensions for ${Name}: $probeOutput"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$dimensions = $probeOutput -split ','
|
||||||
|
return [pscustomobject]@{ Width = [int]$dimensions[0]; Height = [int]$dimensions[1] }
|
||||||
|
}
|
||||||
|
|
||||||
Test-VideoExists -Path $S1 -Name "S1"
|
Test-VideoExists -Path $S1 -Name "S1"
|
||||||
Test-VideoExists -Path $S2 -Name "S2"
|
Test-VideoExists -Path $S2 -Name "S2"
|
||||||
|
|
||||||
@@ -226,16 +255,9 @@ elseif ($S3) { $videoCount = 3 }
|
|||||||
# --- Detect orientation of S1 ---
|
# --- Detect orientation of S1 ---
|
||||||
|
|
||||||
Write-Host "Detecting orientation of S1..." -ForegroundColor Cyan
|
Write-Host "Detecting orientation of S1..." -ForegroundColor Cyan
|
||||||
$probeOutput = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S1 2>&1
|
$dim1 = Get-VideoDimensions -Path $S1 -Name "S1"
|
||||||
|
$width = $dim1.Width
|
||||||
if ($LASTEXITCODE -ne 0) {
|
$height = $dim1.Height
|
||||||
Write-Error "Failed to probe video dimensions for S1: $probeOutput"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
$dimensions = $probeOutput -split ','
|
|
||||||
$width = [int]$dimensions[0]
|
|
||||||
$height = [int]$dimensions[1]
|
|
||||||
|
|
||||||
$isPortrait = $height -gt $width
|
$isPortrait = $height -gt $width
|
||||||
|
|
||||||
@@ -256,6 +278,34 @@ if ($isPortrait) {
|
|||||||
Write-Host "Landscape mode detected (${width}x${height}) - Output: $resolutionLabel" -ForegroundColor Green
|
Write-Host "Landscape mode detected (${width}x${height}) - Output: $resolutionLabel" -ForegroundColor Green
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# S2 dimensions are needed for the exact-fit check below and for -Clip.
|
||||||
|
$w2 = 0
|
||||||
|
$h2 = 0
|
||||||
|
if ($videoCount -eq 2 -or $Clip) {
|
||||||
|
$dim2 = Get-VideoDimensions -Path $S2 -Name "S2"
|
||||||
|
$w2 = $dim2.Width
|
||||||
|
$h2 = $dim2.Height
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Exact fit: two sources that already tile the output perfectly ---
|
||||||
|
# When both sources are exactly as tall as the output and their widths add up to the
|
||||||
|
# output width (e.g. 1280x1080 + 640x1080 at 1K HD), stacking them side by side already
|
||||||
|
# produces the target frame. Scaling or padding would only degrade them, so the sources
|
||||||
|
# are passed straight to hstack untouched. -Clip and -Sideways have nothing to do in this
|
||||||
|
# case (there is no padding to remove and the layout is already a single row).
|
||||||
|
$exactFit = ($videoCount -eq 2 -and $height -eq $outputHeight -and $h2 -eq $outputHeight -and ($width + $w2) -eq $outputWidth)
|
||||||
|
|
||||||
|
if ($exactFit) {
|
||||||
|
Write-Host ("Exact fit: {0}x{1} + {2}x{3} already tile {4}x{5}; sources used unscaled." -f $width, $height, $w2, $h2, $outputWidth, $outputHeight) -ForegroundColor Green
|
||||||
|
if ($Clip) {
|
||||||
|
Write-Host "Exact fit: -Clip has no effect (output contains no padding)." -ForegroundColor Yellow
|
||||||
|
$Clip = $false
|
||||||
|
}
|
||||||
|
if ($Sideways) {
|
||||||
|
Write-Host "Exact fit: -Sideways has no effect (sources are already a single row)." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($Clip -and -not $Sideways) {
|
if ($Clip -and -not $Sideways) {
|
||||||
$clipAllowed = ($isPortrait -and $videoCount -le 4) -or (-not $isPortrait -and $videoCount -eq 2)
|
$clipAllowed = ($isPortrait -and $videoCount -le 4) -or (-not $isPortrait -and $videoCount -eq 2)
|
||||||
if (-not $clipAllowed) {
|
if (-not $clipAllowed) {
|
||||||
@@ -268,16 +318,6 @@ if ($Clip -and -not $Sideways) {
|
|||||||
$clipMixedLandscape = $false
|
$clipMixedLandscape = $false
|
||||||
$clipMixedFilterComplex = $null
|
$clipMixedFilterComplex = $null
|
||||||
if ($Clip -and -not $Sideways) {
|
if ($Clip -and -not $Sideways) {
|
||||||
Write-Host "Probing S2 dimensions for -Clip..." -ForegroundColor Cyan
|
|
||||||
$probeOutput2 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S2 2>&1
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Error "Failed to probe video dimensions for S2: $probeOutput2"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
$dim2 = $probeOutput2 -split ','
|
|
||||||
$w2 = [int]$dim2[0]
|
|
||||||
$h2 = [int]$dim2[1]
|
|
||||||
|
|
||||||
$s1IsLandscape = $width -gt $height
|
$s1IsLandscape = $width -gt $height
|
||||||
$s2IsLandscape = $w2 -gt $h2
|
$s2IsLandscape = $w2 -gt $h2
|
||||||
$isMixedOrientation = ($s1IsLandscape -xor $s2IsLandscape)
|
$isMixedOrientation = ($s1IsLandscape -xor $s2IsLandscape)
|
||||||
@@ -364,7 +404,11 @@ if ($Clip -and -not $Sideways) {
|
|||||||
$filterComplex = ""
|
$filterComplex = ""
|
||||||
$inputArgs = @("-i", $S1, "-i", $S2)
|
$inputArgs = @("-i", $S1, "-i", $S2)
|
||||||
|
|
||||||
if ($Sideways) {
|
if ($exactFit) {
|
||||||
|
# Both sources already have the exact output height and their widths sum to the output
|
||||||
|
# width: hstack them as-is, with no scale and no pad filters in the graph.
|
||||||
|
$filterComplex = "[0:v][1:v]hstack=inputs=2[outv]"
|
||||||
|
} elseif ($Sideways) {
|
||||||
Write-Host "Sideways mode: arranging $videoCount videos in a single row." -ForegroundColor Cyan
|
Write-Host "Sideways mode: arranging $videoCount videos in a single row." -ForegroundColor Cyan
|
||||||
|
|
||||||
if ($isPortrait) {
|
if ($isPortrait) {
|
||||||
@@ -457,13 +501,8 @@ if ($Sideways) {
|
|||||||
$inputArgs += @("-i", $S3)
|
$inputArgs += @("-i", $S3)
|
||||||
$applyPortraitClip = $false
|
$applyPortraitClip = $false
|
||||||
if ($Clip) {
|
if ($Clip) {
|
||||||
$probeOutput3 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S3 2>&1
|
$dim3 = Get-VideoDimensions -Path $S3 -Name "S3"
|
||||||
if ($LASTEXITCODE -ne 0) {
|
$w3 = $dim3.Width; $h3 = $dim3.Height
|
||||||
Write-Error "Failed to probe video dimensions for S3: $probeOutput3"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
$dim3 = $probeOutput3 -split ','
|
|
||||||
$w3 = [int]$dim3[0]; $h3 = [int]$dim3[1]
|
|
||||||
if (($h2 -gt $w2) -and ($h3 -gt $w3)) {
|
if (($h2 -gt $w2) -and ($h3 -gt $w3)) {
|
||||||
$applyPortraitClip = $true
|
$applyPortraitClip = $true
|
||||||
} else {
|
} else {
|
||||||
@@ -490,20 +529,10 @@ if ($Sideways) {
|
|||||||
$inputArgs += @("-i", $S3, "-i", $S4)
|
$inputArgs += @("-i", $S3, "-i", $S4)
|
||||||
$applyPortraitClip4 = $false
|
$applyPortraitClip4 = $false
|
||||||
if ($Clip) {
|
if ($Clip) {
|
||||||
$probeOutput3 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S3 2>&1
|
$dim3 = Get-VideoDimensions -Path $S3 -Name "S3"
|
||||||
if ($LASTEXITCODE -ne 0) {
|
$w3 = $dim3.Width; $h3 = $dim3.Height
|
||||||
Write-Error "Failed to probe video dimensions for S3: $probeOutput3"
|
$dim4 = Get-VideoDimensions -Path $S4 -Name "S4"
|
||||||
exit 1
|
$w4 = $dim4.Width; $h4 = $dim4.Height
|
||||||
}
|
|
||||||
$dim3 = $probeOutput3 -split ','
|
|
||||||
$w3 = [int]$dim3[0]; $h3 = [int]$dim3[1]
|
|
||||||
$probeOutput4 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S4 2>&1
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Error "Failed to probe video dimensions for S4: $probeOutput4"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
$dim4 = $probeOutput4 -split ','
|
|
||||||
$w4 = [int]$dim4[0]; $h4 = [int]$dim4[1]
|
|
||||||
if (($h2 -gt $w2) -and ($h3 -gt $w3) -and ($h4 -gt $w4)) {
|
if (($h2 -gt $w2) -and ($h3 -gt $w3) -and ($h4 -gt $w4)) {
|
||||||
$applyPortraitClip4 = $true
|
$applyPortraitClip4 = $true
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in new issue
Block a user