diff --git a/src/NuGet.Config b/src/NuGet.Config
index e07e77531..4bf5263ac 100644
--- a/src/NuGet.Config
+++ b/src/NuGet.Config
@@ -5,6 +5,5 @@
-
diff --git a/src/NzbDrone.Common/Sonarr.Common.csproj b/src/NzbDrone.Common/Sonarr.Common.csproj
index d9ab36fd5..e4cbd0558 100644
--- a/src/NzbDrone.Common/Sonarr.Common.csproj
+++ b/src/NzbDrone.Common/Sonarr.Common.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs
index 49a3b06d9..126399b51 100644
--- a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs
+++ b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs
@@ -1,12 +1,11 @@
+using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Reflection;
-using FFMpegCore;
+using System.Text.Json.Nodes;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
-using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.Categories;
@@ -103,39 +102,39 @@ public void get_info_unicode()
info.Title.Should().Be("Sample Title");
}
- [TestCase(8, "", "", "", null, HdrFormat.None)]
- [TestCase(10, "", "", "", null, HdrFormat.None)]
- [TestCase(10, "bt709", "bt709", "", null, HdrFormat.None)]
- [TestCase(8, "bt2020", "smpte2084", "", null, HdrFormat.None)]
- [TestCase(10, "bt2020", "bt2020-10", "", null, HdrFormat.None)]
- [TestCase(10, "bt2020", "arib-std-b67", "", null, HdrFormat.Hlg10)]
- [TestCase(10, "bt2020", "smpte2084", "", null, HdrFormat.Pq10)]
- [TestCase(10, "bt2020", "smpte2084", "FFMpegCore.SideData", null, HdrFormat.Pq10)]
- [TestCase(10, "bt2020", "smpte2084", "FFMpegCore.MasteringDisplayMetadata", null, HdrFormat.Hdr10)]
- [TestCase(10, "bt2020", "smpte2084", "FFMpegCore.ContentLightLevelMetadata", null, HdrFormat.Hdr10)]
- [TestCase(10, "bt2020", "smpte2084", "FFMpegCore.HdrDynamicMetadataSpmte2094", null, HdrFormat.Hdr10Plus)]
- [TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData", null, HdrFormat.DolbyVision)]
- [TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData", 1, HdrFormat.DolbyVisionHdr10)]
- [TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData,FFMpegCore.HdrDynamicMetadataSpmte2094", 1, HdrFormat.DolbyVisionHdr10Plus)]
- [TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData,FFMpegCore.HdrDynamicMetadataSpmte2094", 6, HdrFormat.DolbyVisionHdr10Plus)]
- [TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData", 2, HdrFormat.DolbyVisionSdr)]
- [TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData", 4, HdrFormat.DolbyVisionHlg)]
- public void should_detect_hdr_correctly(int bitDepth, string colourPrimaries, string transferFunction, string sideDataTypes, int? doviConfigId, HdrFormat expected)
+ [TestCase(8, "", "", null, null, HdrFormat.None)]
+ [TestCase(10, "", "", null, null, HdrFormat.None)]
+ [TestCase(10, "bt709", "bt709", null, null, HdrFormat.None)]
+ [TestCase(8, "bt2020", "smpte2084", null, null, HdrFormat.None)]
+ [TestCase(10, "bt2020", "bt2020-10", null, null, HdrFormat.None)]
+ [TestCase(10, "bt2020", "arib-std-b67", null, null, HdrFormat.Hlg10)]
+ [TestCase(10, "bt2020", "smpte2084", null, null, HdrFormat.Pq10)]
+ [TestCase(10, "bt2020", "smpte2084", new[] { "" }, null, HdrFormat.Pq10)]
+ [TestCase(10, "bt2020", "smpte2084", new[] { FFMpegCoreSideDataTypes.MasteringDisplayMetadata }, null, HdrFormat.Hdr10)]
+ [TestCase(10, "bt2020", "smpte2084", new[] { FFMpegCoreSideDataTypes.ContentLightLevelMetadata }, null, HdrFormat.Hdr10)]
+ [TestCase(10, "bt2020", "smpte2084", new[] { FFMpegCoreSideDataTypes.HdrDynamicMetadataSpmte2094 }, null, HdrFormat.Hdr10Plus)]
+ [TestCase(10, "bt2020", "smpte2084", new[] { FFMpegCoreSideDataTypes.DoviConfigurationRecordSideData }, null, HdrFormat.DolbyVision)]
+ [TestCase(10, "bt2020", "smpte2084", new[] { FFMpegCoreSideDataTypes.DoviConfigurationRecordSideData }, 1, HdrFormat.DolbyVisionHdr10)]
+ [TestCase(10, "bt2020", "smpte2084", new[] { FFMpegCoreSideDataTypes.DoviConfigurationRecordSideData, FFMpegCoreSideDataTypes.HdrDynamicMetadataSpmte2094 }, 1, HdrFormat.DolbyVisionHdr10Plus)]
+ [TestCase(10, "bt2020", "smpte2084", new[] { FFMpegCoreSideDataTypes.DoviConfigurationRecordSideData, FFMpegCoreSideDataTypes.HdrDynamicMetadataSpmte2094 }, 6, HdrFormat.DolbyVisionHdr10Plus)]
+ [TestCase(10, "bt2020", "smpte2084", new[] { FFMpegCoreSideDataTypes.DoviConfigurationRecordSideData }, 2, HdrFormat.DolbyVisionSdr)]
+ [TestCase(10, "bt2020", "smpte2084", new[] { FFMpegCoreSideDataTypes.DoviConfigurationRecordSideData }, 4, HdrFormat.DolbyVisionHlg)]
+ public void should_detect_hdr_correctly(int bitDepth, string colourPrimaries, string transferFunction, string[] sideDataTypes, int? doviConfigId, HdrFormat expected)
{
- var assembly = Assembly.GetAssembly(typeof(FFProbe));
- var types = sideDataTypes.Split(",").Select(x => x.Trim()).ToList();
- var sideData = types.Where(x => x.IsNotNullOrWhiteSpace()).Select(x => assembly.CreateInstance(x)).Cast().ToList();
-
- if (doviConfigId.HasValue)
+ var sideData = sideDataTypes?.Select(sideDataType =>
{
- sideData.ForEach(x =>
+ var sideData = new Dictionary
{
- if (x.GetType().Name == "DoviConfigurationRecordSideData")
- {
- ((DoviConfigurationRecordSideData)x).DvBlSignalCompatibilityId = doviConfigId.Value;
- }
- });
- }
+ { "side_data_type", JsonValue.Create(sideDataType) }
+ };
+
+ if (doviConfigId.HasValue)
+ {
+ sideData.Add("dv_bl_signal_compatibility_id", JsonValue.Create(doviConfigId.Value));
+ }
+
+ return sideData;
+ }).ToList();
var result = VideoFileInfoReader.GetHdrFormat(bitDepth, colourPrimaries, transferFunction, sideData);
diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs
index 203bd6714..fff7f11bb 100644
--- a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs
+++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs
@@ -52,7 +52,11 @@ public static string FormatAudioCodec(MediaInfoModel mediaInfo, string sceneName
if (audioFormat == "truehd")
{
- return "TrueHD";
+ return audioProfile switch
+ {
+ "Dolby TrueHD + Dolby Atmos" => "TrueHD Atmos",
+ _ => "TrueHD"
+ };
}
if (audioFormat == "flac")
@@ -62,7 +66,7 @@ public static string FormatAudioCodec(MediaInfoModel mediaInfo, string sceneName
if (audioFormat == "dts")
{
- if (audioProfile == "DTS:X")
+ if (audioProfile is "DTS:X" or "DTS-HD MA + DTS:X IMAX")
{
return "DTS-X";
}
@@ -102,7 +106,11 @@ public static string FormatAudioCodec(MediaInfoModel mediaInfo, string sceneName
if (audioFormat == "eac3")
{
- return "EAC3";
+ return audioProfile switch
+ {
+ "Dolby Digital Plus + Dolby Atmos" => "EAC3 Atmos",
+ _ => "EAC3"
+ };
}
if (audioFormat == "ac3")
@@ -117,6 +125,11 @@ public static string FormatAudioCodec(MediaInfoModel mediaInfo, string sceneName
return "HE-AAC";
}
+ if (audioProfile == "xHE-AAC")
+ {
+ return "xHE-AAC";
+ }
+
return "AAC";
}
diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs
index 41b40596d..197c9b88c 100644
--- a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs
+++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
-using FFMpegCore;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.MediaFiles.MediaInfo
@@ -9,7 +8,7 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
public class MediaInfoModel : IEmbeddedDocument
{
public string RawStreamData { get; set; }
- public string RawFrameData { get; set; }
+
public int SchemaRevision { get; set; }
public string ContainerFormat { get; set; }
@@ -27,8 +26,6 @@ public class MediaInfoModel : IEmbeddedDocument
public string VideoTransferCharacteristics { get; set; }
- public DoviConfigurationRecordSideData DoviConfigurationRecord { get; set; }
-
public HdrFormat VideoHdrFormat { get; set; }
public int Height { get; set; }
diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs
index 2b9379db7..deef4daca 100644
--- a/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs
+++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text.Json.Nodes;
using FFMpegCore;
using NLog;
using NzbDrone.Common.Disk;
@@ -19,7 +20,6 @@ public class VideoFileInfoReader : IVideoFileInfoReader
{
private readonly IDiskProvider _diskProvider;
private readonly Logger _logger;
- private readonly List _pixelFormats;
public const int MINIMUM_MEDIA_INFO_SCHEMA_REVISION = 12;
public const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 12;
@@ -36,16 +36,6 @@ public VideoFileInfoReader(IDiskProvider diskProvider, Logger logger)
// We bundle ffprobe for all platforms
GlobalFFOptions.Configure(options => options.BinaryFolder = AppDomain.CurrentDomain.BaseDirectory);
-
- try
- {
- _pixelFormats = FFProbe.GetPixelFormats();
- }
- catch (Exception e)
- {
- _logger.Error(e, "Failed to get supported pixel formats from ffprobe");
- _pixelFormats = new List();
- }
}
public MediaInfoModel GetMediaInfo(string filename)
@@ -65,15 +55,13 @@ public MediaInfoModel GetMediaInfo(string filename)
try
{
_logger.Debug("Getting media info from {0}", filename);
- var ffprobeOutput = FFProbe.GetStreamJson(filename, ffOptions: new FFOptions { ExtraArguments = "-probesize 50000000" });
- var analysis = FFProbe.AnalyseStreamJson(ffprobeOutput);
+ var analysis = FFProbe.Analyse(filename, customArguments: "-probesize 50000000");
var primaryVideoStream = GetPrimaryVideoStream(analysis);
if (analysis.PrimaryAudioStream?.ChannelLayout.IsNullOrWhiteSpace() ?? true)
{
- ffprobeOutput = FFProbe.GetStreamJson(filename, ffOptions: new FFOptions { ExtraArguments = "-probesize 150000000 -analyzeduration 150000000" });
- analysis = FFProbe.AnalyseStreamJson(ffprobeOutput);
+ analysis = FFProbe.Analyse(filename, customArguments: "-probesize 150000000 -analyzeduration 150000000");
}
var mediaInfoModel = new MediaInfoModel();
@@ -85,7 +73,6 @@ public MediaInfoModel GetMediaInfo(string filename)
mediaInfoModel.VideoBitDepth = GetPixelFormat(primaryVideoStream?.PixelFormat)?.Components.Min(x => x.BitDepth) ?? 8;
mediaInfoModel.VideoColourPrimaries = primaryVideoStream?.ColorPrimaries;
mediaInfoModel.VideoTransferCharacteristics = primaryVideoStream?.ColorTransfer;
- mediaInfoModel.DoviConfigurationRecord = primaryVideoStream?.SideDataList?.Find(x => x.GetType().Name == nameof(DoviConfigurationRecordSideData)) as DoviConfigurationRecordSideData;
mediaInfoModel.Height = primaryVideoStream?.Height ?? 0;
mediaInfoModel.Width = primaryVideoStream?.Width ?? 0;
mediaInfoModel.AudioFormat = analysis.PrimaryAudioStream?.CodecName;
@@ -103,8 +90,12 @@ public MediaInfoModel GetMediaInfo(string filename)
mediaInfoModel.Subtitles = analysis.SubtitleStreams?.Select(x => x.Language)
.Where(l => l.IsNotNullOrWhiteSpace())
.ToList();
- mediaInfoModel.ScanType = "Progressive";
- mediaInfoModel.RawStreamData = ffprobeOutput;
+ mediaInfoModel.ScanType = primaryVideoStream?.FieldOrder switch
+ {
+ "tt" or "bb" or "tb" or "bt" => "Interlaced",
+ _ => "Progressive"
+ };
+ mediaInfoModel.RawStreamData = string.Concat(analysis.OutputData);
mediaInfoModel.SchemaRevision = CURRENT_MEDIA_INFO_SCHEMA_REVISION;
if (analysis.Format.Tags?.TryGetValue("title", out var title) ?? false)
@@ -117,14 +108,11 @@ public MediaInfoModel GetMediaInfo(string filename)
// if it looks like PQ10 or similar HDR, do a frame analysis to figure out which type it is
if (PqTransferFunctions.Contains(mediaInfoModel.VideoTransferCharacteristics))
{
- var frameOutput = FFProbe.GetFrameJson(filename, ffOptions: new() { ExtraArguments = $"-read_intervals \"%+#1\" -select_streams v:{primaryVideoStream?.Index ?? 0}" });
- mediaInfoModel.RawFrameData = frameOutput;
-
- frames = FFProbe.AnalyseFrameJson(frameOutput);
+ frames = FFProbe.GetFrames(filename, customArguments: $"-read_intervals \"%+#1\" -select_streams v:{primaryVideoStream?.Index ?? 0}");
}
- var streamSideData = primaryVideoStream?.SideDataList ?? new();
- var framesSideData = frames?.Frames?.Count > 0 ? frames?.Frames[0]?.SideDataList ?? new() : new();
+ var streamSideData = primaryVideoStream?.SideData ?? new();
+ var framesSideData = frames?.Frames.FirstOrDefault()?.SideData ?? new();
var sideData = streamSideData.Concat(framesSideData).ToList();
mediaInfoModel.VideoHdrFormat = GetHdrFormat(mediaInfoModel.VideoBitDepth, mediaInfoModel.VideoColourPrimaries, mediaInfoModel.VideoTransferCharacteristics, sideData);
@@ -176,7 +164,7 @@ private static long GetBitrate(MediaStream mediaStream)
return 0;
}
- private VideoStream GetPrimaryVideoStream(IMediaAnalysis mediaAnalysis)
+ private static VideoStream GetPrimaryVideoStream(IMediaAnalysis mediaAnalysis)
{
if (mediaAnalysis.VideoStreams.Count <= 1)
{
@@ -189,23 +177,30 @@ private VideoStream GetPrimaryVideoStream(IMediaAnalysis mediaAnalysis)
return mediaAnalysis.VideoStreams.FirstOrDefault(s => !codecFilter.Contains(s.CodecName)) ?? mediaAnalysis.PrimaryVideoStream;
}
- private FFProbePixelFormat GetPixelFormat(string format)
+ private static FFProbePixelFormat GetPixelFormat(string format)
{
- return _pixelFormats.Find(x => x.Name == format);
+ if (format.IsNullOrWhiteSpace())
+ {
+ return null;
+ }
+
+ return FFProbe.TryGetPixelFormat(format, out var pixelFormat) ? pixelFormat : null;
}
- public static HdrFormat GetHdrFormat(int bitDepth, string colorPrimaries, string transferFunction, List sideData)
+ public static HdrFormat GetHdrFormat(int bitDepth, string colorPrimaries, string transferFunction, List> sideData)
{
if (bitDepth < 10)
{
return HdrFormat.None;
}
- if (TryGetSideData(sideData, out var dovi))
+ if (TryGetSideData(sideData, FFMpegCoreSideDataTypes.DoviConfigurationRecordSideData, out var dovi))
{
- var hasHdr10Plus = TryGetSideData(sideData, out _);
+ var hasHdr10Plus = TryGetSideData(sideData, FFMpegCoreSideDataTypes.HdrDynamicMetadataSpmte2094, out _);
- return dovi.DvBlSignalCompatibilityId switch
+ dovi.TryGetValue("dv_bl_signal_compatibility_id", out var dvBlSignalCompatibilityId);
+
+ return dvBlSignalCompatibilityId?.GetValue() switch
{
1 => hasHdr10Plus ? HdrFormat.DolbyVisionHdr10Plus : HdrFormat.DolbyVisionHdr10,
2 => HdrFormat.DolbyVisionSdr,
@@ -227,13 +222,13 @@ public static HdrFormat GetHdrFormat(int bitDepth, string colorPrimaries, string
if (PqTransferFunctions.Contains(transferFunction))
{
- if (TryGetSideData(sideData, out _))
+ if (TryGetSideData(sideData, FFMpegCoreSideDataTypes.HdrDynamicMetadataSpmte2094, out _))
{
return HdrFormat.Hdr10Plus;
}
- if (TryGetSideData(sideData, out _) ||
- TryGetSideData(sideData, out _))
+ if (TryGetSideData(sideData, FFMpegCoreSideDataTypes.MasteringDisplayMetadata, out _) ||
+ TryGetSideData(sideData, FFMpegCoreSideDataTypes.ContentLightLevelMetadata, out _))
{
return HdrFormat.Hdr10;
}
@@ -244,12 +239,21 @@ public static HdrFormat GetHdrFormat(int bitDepth, string colorPrimaries, string
return HdrFormat.None;
}
- private static bool TryGetSideData(List list, out T result)
- where T : SideData
+ private static bool TryGetSideData(IReadOnlyList> list, string name, out Dictionary result)
{
- result = (T)list?.FirstOrDefault(x => x.GetType().Name == typeof(T).Name);
+ result = list?.FirstOrDefault(item =>
+ item.TryGetValue("side_data_type", out var rawSideDataType) &&
+ rawSideDataType.GetValue().Equals(name, StringComparison.OrdinalIgnoreCase));
return result != null;
}
}
+
+ public sealed class FFMpegCoreSideDataTypes
+ {
+ public const string DoviConfigurationRecordSideData = "DOVI configuration record";
+ public const string HdrDynamicMetadataSpmte2094 = "HDR Dynamic Metadata SMPTE2094-40 (HDR10+)";
+ public const string MasteringDisplayMetadata = "Mastering display metadata";
+ public const string ContentLightLevelMetadata = "Content light level metadata";
+ }
}
diff --git a/src/NzbDrone.Core/Sonarr.Core.csproj b/src/NzbDrone.Core/Sonarr.Core.csproj
index 95d383318..12fd12bb7 100644
--- a/src/NzbDrone.Core/Sonarr.Core.csproj
+++ b/src/NzbDrone.Core/Sonarr.Core.csproj
@@ -9,9 +9,9 @@
+
+
-
-
@@ -25,7 +25,7 @@
-
+