Add mixed-orientation layout for -Clip mode with landscape video at native size and portrait video scaled to match height
This commit is contained in:
+87
-21
@@ -58,8 +58,19 @@
|
||||
the top/bottom black letterbox padding. Only applies when exactly 2 source videos are
|
||||
provided; ignored otherwise.
|
||||
|
||||
Example: two 1280x720 (16:9) sources rendered at 1K HD become 1920x540 instead of
|
||||
1920x1080. The same pair rendered with -In4K becomes 3840x1080 instead of 3840x2160.
|
||||
Behavior depends on the orientations of the two sources:
|
||||
|
||||
- Both same orientation (both landscape or both portrait): the output height is set
|
||||
to the larger of the two sources' scaled heights inside their half-width cells.
|
||||
Example: two 1280x720 sources -> 1920x540 (1K) or 3840x1080 (4K).
|
||||
|
||||
- Mixed orientations (one landscape, one portrait), and the landscape source is
|
||||
smaller than the target resolution: the landscape video occupies its native pixel
|
||||
width at 1K HD (or 2x in 4K), and the portrait video is scaled to match the
|
||||
landscape's height and centered in the remaining width with black pillar boxes.
|
||||
Example: 1280x720 landscape + 720x1280 portrait at 1K HD -> 1920x720 (landscape
|
||||
occupies 1280x720 on its S1/S2 side, portrait fits in the remaining 640x720).
|
||||
At 4K the same pair becomes 3840x1440 (landscape 2560x1440, portrait cell 1280x1440).
|
||||
|
||||
.EXAMPLE
|
||||
.\Compare-Videos.ps1 -S1 original.mp4 -S2 enhanced.mp4 -Output comparison.mp4
|
||||
@@ -186,6 +197,8 @@ if ($isPortrait) {
|
||||
}
|
||||
|
||||
# --- Clip mode: shrink output height to the actual scaled height of the sources ---
|
||||
$clipMixedLandscape = $false
|
||||
$clipMixedFilterComplex = $null
|
||||
if ($Clip) {
|
||||
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
|
||||
@@ -197,34 +210,87 @@ if ($Clip) {
|
||||
$w2 = [int]$dim2[0]
|
||||
$h2 = [int]$dim2[1]
|
||||
|
||||
# Per-source cell width matches the 2-video branches below.
|
||||
if ($isPortrait) {
|
||||
$clipCellWidth = [int]($outputWidth * 0.6333 / 2)
|
||||
} else {
|
||||
$clipCellWidth = [int]($outputWidth / 2)
|
||||
$s1IsLandscape = $width -gt $height
|
||||
$s2IsLandscape = $w2 -gt $h2
|
||||
$isMixedOrientation = ($s1IsLandscape -xor $s2IsLandscape)
|
||||
|
||||
if ($isMixedOrientation) {
|
||||
# One source is landscape, the other is portrait. Try the mixed-orientation
|
||||
# layout: the landscape video keeps its native pixel size at 1K HD (or 2x in 4K),
|
||||
# and the portrait video is scaled to match the landscape's height and centered
|
||||
# in the remaining width with black pillar-box bars.
|
||||
$scaleFactor = $outputWidth / 1920.0 # 1.0 at 1K HD, 2.0 at 4K UHD
|
||||
|
||||
if ($s1IsLandscape) {
|
||||
$landscapeW = $width; $landscapeH = $height
|
||||
} else {
|
||||
$landscapeW = $w2; $landscapeH = $h2
|
||||
}
|
||||
|
||||
$landscapeOutW = [int][Math]::Floor($landscapeW * $scaleFactor)
|
||||
$landscapeOutH = [int][Math]::Floor($landscapeH * $scaleFactor)
|
||||
if ($landscapeOutW % 2) { $landscapeOutW++ }
|
||||
if ($landscapeOutH % 2) { $landscapeOutH++ }
|
||||
|
||||
if ($landscapeOutW -lt $outputWidth -and $landscapeOutH -le $outputHeight) {
|
||||
$portraitCellW = $outputWidth - $landscapeOutW
|
||||
$outputHeight = $landscapeOutH
|
||||
|
||||
Write-Host ("Clip mode (mixed orientation): landscape {0}x{1} + portrait {2}x{1} -> {3}x{1}" -f $landscapeOutW, $landscapeOutH, $portraitCellW, $outputWidth) -ForegroundColor Yellow
|
||||
|
||||
# Preserve S1=left, S2=right ordering. Each input is scaled into its cell
|
||||
# with aspect-ratio preserved, then padded with black to exactly fill the cell.
|
||||
if ($s1IsLandscape) {
|
||||
$leftW = $landscapeOutW; $rightW = $portraitCellW
|
||||
} else {
|
||||
$leftW = $portraitCellW; $rightW = $landscapeOutW
|
||||
}
|
||||
$clipMixedFilterComplex = "[0:v]scale=${leftW}:${landscapeOutH}:force_original_aspect_ratio=decrease,pad=${leftW}:${landscapeOutH}:(ow-iw)/2:(oh-ih)/2:black[v0];" +
|
||||
"[1:v]scale=${rightW}:${landscapeOutH}:force_original_aspect_ratio=decrease,pad=${rightW}:${landscapeOutH}:(ow-iw)/2:(oh-ih)/2:black[v1];" +
|
||||
"[v0][v1]hstack=inputs=2[outv]"
|
||||
$clipMixedLandscape = $true
|
||||
} else {
|
||||
Write-Host "Clip mode: mixed orientations detected, but the landscape source already fills the target width; using standard clip layout." -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
# Height each source would naturally scale to inside its cell when fitted by width
|
||||
# (force_original_aspect_ratio=decrease). Take the larger of the two so neither
|
||||
# source loses content; cap at the original output height.
|
||||
$h1Scaled = [int][Math]::Floor($clipCellWidth * $height / $width)
|
||||
$h2Scaled = [int][Math]::Floor($clipCellWidth * $h2 / $w2)
|
||||
$clippedHeight = [Math]::Max($h1Scaled, $h2Scaled)
|
||||
if ($clippedHeight % 2) { $clippedHeight++ } # libx264/yuv420p requires even dimensions
|
||||
if ($clippedHeight -gt $outputHeight) { $clippedHeight = $outputHeight }
|
||||
if (-not $clipMixedLandscape) {
|
||||
# Standard -Clip behaviour: both sources share an orientation (or mixed fell back).
|
||||
# Compute the largest natural scaled height across both sources and reduce the
|
||||
# output height to that value.
|
||||
|
||||
if ($clippedHeight -lt $outputHeight) {
|
||||
Write-Host ("Clip mode: output height reduced from {0} to {1} ({2}x{1})" -f $outputHeight, $clippedHeight, $outputWidth) -ForegroundColor Yellow
|
||||
$outputHeight = $clippedHeight
|
||||
} else {
|
||||
Write-Host "Clip mode: sources already fill the full output height; no clipping applied." -ForegroundColor Yellow
|
||||
# Per-source cell width matches the 2-video branches below.
|
||||
if ($isPortrait) {
|
||||
$clipCellWidth = [int]($outputWidth * 0.6333 / 2)
|
||||
} else {
|
||||
$clipCellWidth = [int]($outputWidth / 2)
|
||||
}
|
||||
|
||||
# Height each source would naturally scale to inside its cell when fitted by width
|
||||
# (force_original_aspect_ratio=decrease). Take the larger of the two so neither
|
||||
# source loses content; cap at the original output height.
|
||||
$h1Scaled = [int][Math]::Floor($clipCellWidth * $height / $width)
|
||||
$h2Scaled = [int][Math]::Floor($clipCellWidth * $h2 / $w2)
|
||||
$clippedHeight = [Math]::Max($h1Scaled, $h2Scaled)
|
||||
if ($clippedHeight % 2) { $clippedHeight++ } # libx264/yuv420p requires even dimensions
|
||||
if ($clippedHeight -gt $outputHeight) { $clippedHeight = $outputHeight }
|
||||
|
||||
if ($clippedHeight -lt $outputHeight) {
|
||||
Write-Host ("Clip mode: output height reduced from {0} to {1} ({2}x{1})" -f $outputHeight, $clippedHeight, $outputWidth) -ForegroundColor Yellow
|
||||
$outputHeight = $clippedHeight
|
||||
} else {
|
||||
Write-Host "Clip mode: sources already fill the full output height; no clipping applied." -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$filterComplex = ""
|
||||
$inputArgs = @("-i", $S1, "-i", $S2)
|
||||
|
||||
if ($isPortrait) {
|
||||
if ($clipMixedLandscape) {
|
||||
# Mixed-orientation -Clip layout was prepared above; use it as-is.
|
||||
$filterComplex = $clipMixedFilterComplex
|
||||
} elseif ($isPortrait) {
|
||||
# Portrait mode scaling - calculate cell dimensions based on output resolution
|
||||
$cellWidth2 = [int]($outputWidth * 0.6333 / 2) # ~608 for 1K, ~1216 for 4K
|
||||
$cellWidth3 = [int]($outputWidth * 0.6333 / 2) # ~608 for 1K, ~1216 for 4K
|
||||
|
||||
+4
-1
@@ -161,7 +161,10 @@ try {
|
||||
$valueDisplay = ('{0} frames ({1}s, {2} fps)' -f $frames, $durationSeconds, $fps)
|
||||
}
|
||||
elseif ($fieldValue -is [array]) {
|
||||
$valueDisplay = ($fieldValue | ConvertTo-Json -Compress -Depth 10)
|
||||
# Pretty-print array values as JSON, prefixed with a newline so the
|
||||
# multi-line output starts cleanly under the field label.
|
||||
$jsonArray = $fieldValue | ConvertTo-Json -Depth 10
|
||||
$valueDisplay = [Environment]::NewLine + $jsonArray
|
||||
}
|
||||
else {
|
||||
$valueDisplay = $fieldValue
|
||||
|
||||
Reference in New Issue
Block a user