Browse Source

fix cropping jellyfin and emby content that is too small (#2481)

* fix cropping jellyfin and emby content that is too small

* fix transcoding tests with nvidia

* update dependencies
pull/2484/head
Jason Dove 8 months ago committed by GitHub
parent
commit
dd5fd1ef8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .config/dotnet-tools.json
  2. 1
      CHANGELOG.md
  3. 2
      ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj
  4. 31
      ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsCalculatorTests.cs
  5. 47
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs
  6. 2
      ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj
  7. 30
      ErsatzTV.FFmpeg/AspectRatio.cs
  8. 2
      ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj
  9. 23
      ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs
  10. 2
      ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj
  11. 4
      ErsatzTV/ErsatzTV.csproj

2
.config/dotnet-tools.json

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"jetbrains.resharper.globaltools": {
"version": "2025.2.1",
"version": "2025.2.2.1",
"commands": [
"jb"
],

1
CHANGELOG.md

@ -101,6 +101,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -101,6 +101,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix some more hls.js warnings by adding codec information to multi-variant playlists
- Fix hardware decode of h264 constrained baseline content using VAAPI accel
- Custom stream selector: ignore embedded text subtitles that have not been extracted
- Fix cropping Jellyfin and Emby content that is smaller than the crop resolution
### Changed
- Filler presets: use separate text fields for `hours`, `minutes` and `seconds` duration

2
ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj

@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

31
ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsCalculatorTests.cs

@ -430,6 +430,37 @@ public class FFmpegPlaybackSettingsCalculatorTests @@ -430,6 +430,37 @@ public class FFmpegPlaybackSettingsCalculatorTests
actual.PadToDesiredResolution.ShouldBeFalse();
}
[Test]
public void Should_ScaleBeyondMinSize_ForCrop_ForTransportStream_UnknownSAR()
{
FFmpegProfile ffmpegProfile = TestProfile() with
{
Resolution = new Resolution { Width = 640, Height = 411 },
ScalingBehavior = ScalingBehavior.Crop
};
var version = new MediaVersion
{ Width = 626, Height = 476, SampleAspectRatio = "0:0", DisplayAspectRatio = "4:3" };
FFmpegPlaybackSettings actual = FFmpegPlaybackSettingsCalculator.CalculateSettings(
StreamingMode.TransportStream,
ffmpegProfile,
version,
new MediaStream(),
DateTimeOffset.Now,
DateTimeOffset.Now,
TimeSpan.Zero,
TimeSpan.Zero,
false,
StreamInputKind.Vod,
None);
IDisplaySize scaledSize = actual.ScaledSize.IfNone(new MediaVersion { Width = 0, Height = 0 });
scaledSize.Width.ShouldBe(640);
scaledSize.Height.ShouldBe(480);
actual.PadToDesiredResolution.ShouldBeFalse();
}
[Test]
public void Should_ScaleDownToMinSize_ForCrop_ForTransportStream()
{

47
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs

@ -92,9 +92,18 @@ public static class FFmpegPlaybackSettingsCalculator @@ -92,9 +92,18 @@ public static class FFmpegPlaybackSettingsCalculator
case StreamingMode.TransportStream:
result.HardwareAcceleration = ffmpegProfile.HardwareAcceleration;
if (NeedToScale(ffmpegProfile, videoVersion) && videoVersion.SampleAspectRatio != "0:0")
string sampleAspectRatio = videoVersion.SampleAspectRatio;
if (sampleAspectRatio == "0:0")
{
DisplaySize scaledSize = CalculateScaledSize(ffmpegProfile, videoVersion);
sampleAspectRatio = AspectRatio.CalculateSAR(
videoVersion.Width,
videoVersion.Height,
videoVersion.DisplayAspectRatio);
}
if (NeedToScale(ffmpegProfile, videoVersion, sampleAspectRatio))
{
DisplaySize scaledSize = CalculateScaledSize(ffmpegProfile, videoVersion, sampleAspectRatio);
if (!scaledSize.IsSameSizeAs(videoVersion))
{
int fixedHeight = scaledSize.Height + scaledSize.Height % 2;
@ -229,11 +238,11 @@ public static class FFmpegPlaybackSettingsCalculator @@ -229,11 +238,11 @@ public static class FFmpegPlaybackSettingsCalculator
FrameRate = 24
};
private static bool NeedToScale(FFmpegProfile ffmpegProfile, MediaVersion version) =>
IsIncorrectSize(ffmpegProfile.Resolution, version) ||
IsTooLarge(ffmpegProfile.Resolution, version) ||
IsOddSize(version) ||
TooSmallToCrop(ffmpegProfile, version);
private static bool NeedToScale(FFmpegProfile ffmpegProfile, MediaVersion version, string sampleAspectRatio) =>
(IsIncorrectSize(ffmpegProfile.Resolution, version) ||
IsTooLarge(ffmpegProfile.Resolution, version) ||
IsOddSize(version) ||
TooSmallToCrop(ffmpegProfile, version)) && sampleAspectRatio != "0:0";
private static bool IsIncorrectSize(Resolution desiredResolution, MediaVersion version) =>
IsAnamorphic(version) ||
@ -257,14 +266,17 @@ public static class FFmpegPlaybackSettingsCalculator @@ -257,14 +266,17 @@ public static class FFmpegPlaybackSettingsCalculator
return version.Height < ffmpegProfile.Resolution.Height || version.Width < ffmpegProfile.Resolution.Width;
}
private static DisplaySize CalculateScaledSize(FFmpegProfile ffmpegProfile, MediaVersion version)
private static DisplaySize CalculateScaledSize(
FFmpegProfile ffmpegProfile,
MediaVersion version,
string sampleAspectRatio)
{
DisplaySize sarSize = SARSize(version);
int p = version.Width * sarSize.Width;
int q = version.Height * sarSize.Height;
(double sarWidth, double sarHeight) = SARSize(sampleAspectRatio);
var p = (int)Math.Round(version.Width * sarWidth);
var q = (int)Math.Round(version.Height * sarHeight);
int g = Gcd(q, p);
p = p / g;
q = q / g;
p /= g;
q /= g;
Resolution targetSize = ffmpegProfile.Resolution;
int hw1 = targetSize.Width;
int hh1 = hw1 * q / p;
@ -323,11 +335,10 @@ public static class FFmpegPlaybackSettingsCalculator @@ -323,11 +335,10 @@ public static class FFmpegPlaybackSettingsCalculator
return version.DisplayAspectRatio != $"{version.Width}:{version.Height}";
}
private static DisplaySize SARSize(MediaVersion version)
private static (double, double) SARSize(string sampleAspectRatio)
{
string[] split = version.SampleAspectRatio.Split(":");
return new DisplaySize(
int.Parse(split[0], CultureInfo.InvariantCulture),
int.Parse(split[1], CultureInfo.InvariantCulture));
string[] split = sampleAspectRatio.Split(":");
return (double.Parse(split[0], CultureInfo.InvariantCulture),
double.Parse(split[1], CultureInfo.InvariantCulture));
}
}

2
ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="5.1.0" />

30
ErsatzTV.FFmpeg/AspectRatio.cs

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
using System.Globalization;
namespace ErsatzTV.FFmpeg;
public static class AspectRatio
{
public static string CalculateSAR(int width, int height, string displayAspectRatio)
{
// first check for decimal DAR
if (!double.TryParse(displayAspectRatio, out double dar))
{
// if not, assume it's a ratio
string[] split = displayAspectRatio.Split(':');
var num = double.Parse(split[0], CultureInfo.InvariantCulture);
var den = double.Parse(split[1], CultureInfo.InvariantCulture);
dar = num / den;
}
double res = width / (double)height;
var formattedDar = string.Format(
CultureInfo.InvariantCulture,
dar % 1 == 0 ? "{0:F0}" : "{0:0.############}",
dar);
var formattedRes = string.Format(
CultureInfo.InvariantCulture,
res % 1 == 0 ? "{0:F0}" : "{0:0.############}",
res);
return $"{formattedDar}:{formattedRes}";
}
}

2
ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="5.1.0" />

23
ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs

@ -17,6 +17,7 @@ using ErsatzTV.Core.Interfaces.Streaming; @@ -17,6 +17,7 @@ using ErsatzTV.Core.Interfaces.Streaming;
using ErsatzTV.Core.Metadata;
using ErsatzTV.FFmpeg;
using ErsatzTV.FFmpeg.Capabilities;
using ErsatzTV.FFmpeg.Capabilities.Nvidia;
using ErsatzTV.FFmpeg.Filter;
using ErsatzTV.FFmpeg.Filter.Cuda;
using ErsatzTV.FFmpeg.Filter.Qsv;
@ -112,7 +113,7 @@ public class TranscodingTests @@ -112,7 +113,7 @@ public class TranscodingTests
public static Watermark[] Watermarks =
[
Watermark.None,
Watermark.PermanentOpaqueScaled,
//Watermark.PermanentOpaqueScaled,
// Watermark.PermanentOpaqueActualSize,
// Watermark.PermanentTransparentScaled,
// Watermark.PermanentTransparentActualSize
@ -121,7 +122,7 @@ public class TranscodingTests @@ -121,7 +122,7 @@ public class TranscodingTests
public static Subtitle[] Subtitles =
[
Subtitle.None,
Subtitle.Picture,
//Subtitle.Picture,
// Subtitle.Text
];
@ -133,8 +134,8 @@ public class TranscodingTests @@ -133,8 +134,8 @@ public class TranscodingTests
public static ScalingBehavior[] ScalingBehaviors =
[
ScalingBehavior.ScaleAndPad
//ScalingBehavior.Crop,
ScalingBehavior.ScaleAndPad,
ScalingBehavior.Crop,
//ScalingBehavior.Stretch
];
@ -150,20 +151,20 @@ public class TranscodingTests @@ -150,20 +151,20 @@ public class TranscodingTests
new("libx264", "yuv420p", "tv", "smpte170m", "bt709", "smpte170m"),
// // //
// // // // example format that requires setparams filter
new("libx264", "yuv420p", string.Empty, string.Empty, string.Empty, string.Empty),
//new("libx264", "yuv420p", string.Empty, string.Empty, string.Empty, string.Empty),
// // //
// // // // new("libx264", "yuvj420p"),
new("libx264", "yuv420p10le"),
//new("libx264", "yuv420p10le"),
// // // // new("libx264", "yuv444p10le"),
// // //
// // // // new("mpeg1video", "yuv420p"),
// // // //
new("mpeg2video", "yuv420p"),
//new("mpeg2video", "yuv420p"),
// //
//new InputFormat("libx265", "yuv420p"),
new("libx265", "yuv420p10le"),
//new("libx265", "yuv420p10le"),
//
new("mpeg4", "yuv420p")
//new("mpeg4", "yuv420p")
//
// new("libvpx-vp9", "yuv420p"),
// new("libvpx-vp9", "yuv420p10le"),
@ -187,7 +188,7 @@ public class TranscodingTests @@ -187,7 +188,7 @@ public class TranscodingTests
public static FFmpegProfileBitDepth[] BitDepths =
[
FFmpegProfileBitDepth.EightBit,
FFmpegProfileBitDepth.TenBit
//FFmpegProfileBitDepth.TenBit
];
public static FFmpegProfileVideoFormat[] VideoFormats =
@ -443,6 +444,8 @@ public class TranscodingTests @@ -443,6 +444,8 @@ public class TranscodingTests
[ValueSource(typeof(TestData), nameof(TestData.StreamingModes))]
StreamingMode streamingMode)
{
NvEncSharpRedirector.Init();
string file = fileToTest;
if (string.IsNullOrWhiteSpace(file))
{

2
ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
<ItemGroup>
<PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="5.1.0" />

4
ErsatzTV/ErsatzTV.csproj

@ -54,10 +54,10 @@ @@ -54,10 +54,10 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MudBlazor" Version="8.12.0" />
<PackageReference Include="MudBlazor" Version="8.13.0" />
<PackageReference Include="NaturalSort.Extension" Version="4.4.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" />
<PackageReference Include="Scalar.AspNetCore" Version="2.8.8" />
<PackageReference Include="Scalar.AspNetCore" Version="2.8.10" />
<PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />

Loading…
Cancel
Save