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
|
||||
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:
|
||||
|
||||
Landscape mode (width > height) - 1K HD:
|
||||
@@ -78,6 +84,9 @@
|
||||
Generate comparison video in 4K UHD (3840x2160) resolution instead of 1K HD (1920x1080).
|
||||
|
||||
.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
|
||||
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
|
||||
@@ -135,6 +144,12 @@
|
||||
|
||||
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
|
||||
.\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 $S2 -Name "S2"
|
||||
|
||||
@@ -226,16 +255,9 @@ elseif ($S3) { $videoCount = 3 }
|
||||
# --- Detect orientation of S1 ---
|
||||
|
||||
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
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Failed to probe video dimensions for S1: $probeOutput"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$dimensions = $probeOutput -split ','
|
||||
$width = [int]$dimensions[0]
|
||||
$height = [int]$dimensions[1]
|
||||
$dim1 = Get-VideoDimensions -Path $S1 -Name "S1"
|
||||
$width = $dim1.Width
|
||||
$height = $dim1.Height
|
||||
|
||||
$isPortrait = $height -gt $width
|
||||
|
||||
@@ -256,6 +278,34 @@ if ($isPortrait) {
|
||||
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) {
|
||||
$clipAllowed = ($isPortrait -and $videoCount -le 4) -or (-not $isPortrait -and $videoCount -eq 2)
|
||||
if (-not $clipAllowed) {
|
||||
@@ -268,16 +318,6 @@ if ($Clip -and -not $Sideways) {
|
||||
$clipMixedLandscape = $false
|
||||
$clipMixedFilterComplex = $null
|
||||
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
|
||||
$s2IsLandscape = $w2 -gt $h2
|
||||
$isMixedOrientation = ($s1IsLandscape -xor $s2IsLandscape)
|
||||
@@ -364,7 +404,11 @@ if ($Clip -and -not $Sideways) {
|
||||
$filterComplex = ""
|
||||
$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
|
||||
|
||||
if ($isPortrait) {
|
||||
@@ -457,13 +501,8 @@ if ($Sideways) {
|
||||
$inputArgs += @("-i", $S3)
|
||||
$applyPortraitClip = $false
|
||||
if ($Clip) {
|
||||
$probeOutput3 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S3 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Failed to probe video dimensions for S3: $probeOutput3"
|
||||
exit 1
|
||||
}
|
||||
$dim3 = $probeOutput3 -split ','
|
||||
$w3 = [int]$dim3[0]; $h3 = [int]$dim3[1]
|
||||
$dim3 = Get-VideoDimensions -Path $S3 -Name "S3"
|
||||
$w3 = $dim3.Width; $h3 = $dim3.Height
|
||||
if (($h2 -gt $w2) -and ($h3 -gt $w3)) {
|
||||
$applyPortraitClip = $true
|
||||
} else {
|
||||
@@ -490,20 +529,10 @@ if ($Sideways) {
|
||||
$inputArgs += @("-i", $S3, "-i", $S4)
|
||||
$applyPortraitClip4 = $false
|
||||
if ($Clip) {
|
||||
$probeOutput3 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S3 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Failed to probe video dimensions for S3: $probeOutput3"
|
||||
exit 1
|
||||
}
|
||||
$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]
|
||||
$dim3 = Get-VideoDimensions -Path $S3 -Name "S3"
|
||||
$w3 = $dim3.Width; $h3 = $dim3.Height
|
||||
$dim4 = Get-VideoDimensions -Path $S4 -Name "S4"
|
||||
$w4 = $dim4.Width; $h4 = $dim4.Height
|
||||
if (($h2 -gt $w2) -and ($h3 -gt $w3) -and ($h4 -gt $w4)) {
|
||||
$applyPortraitClip4 = $true
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user