Merge pull request #16318 from NoFear0411/fix-sar-anamorphic-detection

Fix near-1:1 SAR values falsely flagged as anamorphic
This commit is contained in:
Bond-009 2026-03-03 20:34:05 +01:00 committed by GitHub
commit e378f63b70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 47 additions and 1 deletions

View File

@ -863,7 +863,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
stream.IsAnamorphic = false;
}
else if (string.Equals(streamInfo.SampleAspectRatio, "1:1", StringComparison.Ordinal))
else if (IsNearSquarePixelSar(streamInfo.SampleAspectRatio))
{
stream.IsAnamorphic = false;
}
@ -1154,6 +1154,34 @@ namespace MediaBrowser.MediaEncoding.Probing
return Math.Abs(d1 - d2) <= variance;
}
/// <summary>
/// Determines whether a sample aspect ratio represents square (or near-square) pixels.
/// Some encoders produce SARs like 3201:3200 for content that is effectively 1:1,
/// which would be falsely classified as anamorphic by an exact string comparison.
/// A 1% tolerance safely covers encoder rounding artifacts while preserving detection
/// of genuine anamorphic content (closest standard is PAL 4:3 at 16:15 = 6.67% off).
/// </summary>
/// <param name="sar">The sample aspect ratio string in "N:D" format.</param>
/// <returns><c>true</c> if the SAR is within 1% of 1:1; otherwise <c>false</c>.</returns>
internal static bool IsNearSquarePixelSar(string sar)
{
if (string.IsNullOrEmpty(sar))
{
return false;
}
var parts = sar.Split(':');
if (parts.Length == 2
&& double.TryParse(parts[0], CultureInfo.InvariantCulture, out var num)
&& double.TryParse(parts[1], CultureInfo.InvariantCulture, out var den)
&& den > 0)
{
return IsClose(num / den, 1.0, 0.01);
}
return string.Equals(sar, "1:1", StringComparison.Ordinal);
}
/// <summary>
/// Gets a frame rate from a string value in ffprobe output
/// This could be a number or in the format of 2997/125.

View File

@ -39,6 +39,23 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
public void GetFrameRate_Success(string value, float? expected)
=> Assert.Equal(expected, ProbeResultNormalizer.GetFrameRate(value));
[Theory]
[InlineData("1:1", true)]
[InlineData("3201:3200", true)]
[InlineData("1215:1216", true)]
[InlineData("1001:1000", true)]
[InlineData("16:15", false)]
[InlineData("8:9", false)]
[InlineData("32:27", false)]
[InlineData("10:11", false)]
[InlineData("64:45", false)]
[InlineData("4:3", false)]
[InlineData("0:1", false)]
[InlineData("", false)]
[InlineData(null, false)]
public void IsNearSquarePixelSar_DetectsCorrectly(string? sar, bool expected)
=> Assert.Equal(expected, ProbeResultNormalizer.IsNearSquarePixelSar(sar));
[Fact]
public void GetMediaInfo_MetaData_Success()
{
@ -123,6 +140,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.Equal(358, res.VideoStream.Height);
Assert.Equal(720, res.VideoStream.Width);
Assert.Equal("2.40:1", res.VideoStream.AspectRatio);
Assert.True(res.VideoStream.IsAnamorphic); // SAR 32:27 — genuinely anamorphic NTSC DVD 16:9
Assert.Equal("yuv420p", res.VideoStream.PixelFormat);
Assert.Equal(31d, res.VideoStream.Level);
Assert.Equal(1, res.VideoStream.RefFrames);