aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.MediaEncoding/Probing
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.MediaEncoding/Probing')
-rw-r--r--MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaFrameInfo.cs184
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaFrameSideDataInfo.cs16
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs50
5 files changed, 241 insertions, 20 deletions
diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
index 1b5b5262a..6f51e1a6a 100644
--- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
+++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.MediaEncoding.Probing
if (result.Streams is not null)
{
- // Convert all dictionaries to case insensitive
+ // Convert all dictionaries to case-insensitive
foreach (var stream in result.Streams)
{
if (stream.Tags is not null)
@@ -70,7 +70,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Converts a dictionary to case insensitive.
+ /// Converts a dictionary to case-insensitive.
/// </summary>
/// <param name="dict">The dict.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
diff --git a/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs b/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs
index d4d153b08..53eea64db 100644
--- a/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs
+++ b/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs
@@ -30,5 +30,12 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <value>The chapters.</value>
[JsonPropertyName("chapters")]
public IReadOnlyList<MediaChapter> Chapters { get; set; }
+
+ /// <summary>
+ /// Gets or sets the frames.
+ /// </summary>
+ /// <value>The streams.</value>
+ [JsonPropertyName("frames")]
+ public IReadOnlyList<MediaFrameInfo> Frames { get; set; }
}
}
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaFrameInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaFrameInfo.cs
new file mode 100644
index 000000000..bed4368ed
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Probing/MediaFrameInfo.cs
@@ -0,0 +1,184 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace MediaBrowser.MediaEncoding.Probing;
+
+/// <summary>
+/// Class MediaFrameInfo.
+/// </summary>
+public class MediaFrameInfo
+{
+ /// <summary>
+ /// Gets or sets the media type.
+ /// </summary>
+ [JsonPropertyName("media_type")]
+ public string? MediaType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the StreamIndex.
+ /// </summary>
+ [JsonPropertyName("stream_index")]
+ public int? StreamIndex { get; set; }
+
+ /// <summary>
+ /// Gets or sets the KeyFrame.
+ /// </summary>
+ [JsonPropertyName("key_frame")]
+ public int? KeyFrame { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Pts.
+ /// </summary>
+ [JsonPropertyName("pts")]
+ public long? Pts { get; set; }
+
+ /// <summary>
+ /// Gets or sets the PtsTime.
+ /// </summary>
+ [JsonPropertyName("pts_time")]
+ public string? PtsTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the BestEffortTimestamp.
+ /// </summary>
+ [JsonPropertyName("best_effort_timestamp")]
+ public long BestEffortTimestamp { get; set; }
+
+ /// <summary>
+ /// Gets or sets the BestEffortTimestampTime.
+ /// </summary>
+ [JsonPropertyName("best_effort_timestamp_time")]
+ public string? BestEffortTimestampTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Duration.
+ /// </summary>
+ [JsonPropertyName("duration")]
+ public int Duration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the DurationTime.
+ /// </summary>
+ [JsonPropertyName("duration_time")]
+ public string? DurationTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the PktPos.
+ /// </summary>
+ [JsonPropertyName("pkt_pos")]
+ public string? PktPos { get; set; }
+
+ /// <summary>
+ /// Gets or sets the PktSize.
+ /// </summary>
+ [JsonPropertyName("pkt_size")]
+ public string? PktSize { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Width.
+ /// </summary>
+ [JsonPropertyName("width")]
+ public int? Width { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Height.
+ /// </summary>
+ [JsonPropertyName("height")]
+ public int? Height { get; set; }
+
+ /// <summary>
+ /// Gets or sets the CropTop.
+ /// </summary>
+ [JsonPropertyName("crop_top")]
+ public int? CropTop { get; set; }
+
+ /// <summary>
+ /// Gets or sets the CropBottom.
+ /// </summary>
+ [JsonPropertyName("crop_bottom")]
+ public int? CropBottom { get; set; }
+
+ /// <summary>
+ /// Gets or sets the CropLeft.
+ /// </summary>
+ [JsonPropertyName("crop_left")]
+ public int? CropLeft { get; set; }
+
+ /// <summary>
+ /// Gets or sets the CropRight.
+ /// </summary>
+ [JsonPropertyName("crop_right")]
+ public int? CropRight { get; set; }
+
+ /// <summary>
+ /// Gets or sets the PixFmt.
+ /// </summary>
+ [JsonPropertyName("pix_fmt")]
+ public string? PixFmt { get; set; }
+
+ /// <summary>
+ /// Gets or sets the SampleAspectRatio.
+ /// </summary>
+ [JsonPropertyName("sample_aspect_ratio")]
+ public string? SampleAspectRatio { get; set; }
+
+ /// <summary>
+ /// Gets or sets the PictType.
+ /// </summary>
+ [JsonPropertyName("pict_type")]
+ public string? PictType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the InterlacedFrame.
+ /// </summary>
+ [JsonPropertyName("interlaced_frame")]
+ public int? InterlacedFrame { get; set; }
+
+ /// <summary>
+ /// Gets or sets the TopFieldFirst.
+ /// </summary>
+ [JsonPropertyName("top_field_first")]
+ public int? TopFieldFirst { get; set; }
+
+ /// <summary>
+ /// Gets or sets the RepeatPict.
+ /// </summary>
+ [JsonPropertyName("repeat_pict")]
+ public int? RepeatPict { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ColorRange.
+ /// </summary>
+ [JsonPropertyName("color_range")]
+ public string? ColorRange { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ColorSpace.
+ /// </summary>
+ [JsonPropertyName("color_space")]
+ public string? ColorSpace { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ColorPrimaries.
+ /// </summary>
+ [JsonPropertyName("color_primaries")]
+ public string? ColorPrimaries { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ColorTransfer.
+ /// </summary>
+ [JsonPropertyName("color_transfer")]
+ public string? ColorTransfer { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ChromaLocation.
+ /// </summary>
+ [JsonPropertyName("chroma_location")]
+ public string? ChromaLocation { get; set; }
+
+ /// <summary>
+ /// Gets or sets the SideDataList.
+ /// </summary>
+ [JsonPropertyName("side_data_list")]
+ public IReadOnlyList<MediaFrameSideDataInfo>? SideDataList { get; set; }
+}
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaFrameSideDataInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaFrameSideDataInfo.cs
new file mode 100644
index 000000000..3f7dd9a69
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Probing/MediaFrameSideDataInfo.cs
@@ -0,0 +1,16 @@
+using System.Text.Json.Serialization;
+
+namespace MediaBrowser.MediaEncoding.Probing;
+
+/// <summary>
+/// Class MediaFrameSideDataInfo.
+/// Currently only records the SideDataType for HDR10+ detection.
+/// </summary>
+public class MediaFrameSideDataInfo
+{
+ /// <summary>
+ /// Gets or sets the SideDataType.
+ /// </summary>
+ [JsonPropertyName("side_data_type")]
+ public string? SideDataType { get; set; }
+}
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index c730f4cda..5784deacd 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -10,6 +10,7 @@ using System.Text.RegularExpressions;
using System.Xml;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
+using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -104,8 +105,9 @@ namespace MediaBrowser.MediaEncoding.Probing
SetSize(data, info);
var internalStreams = data.Streams ?? Array.Empty<MediaStreamInfo>();
+ var internalFrames = data.Frames ?? Array.Empty<MediaFrameInfo>();
- info.MediaStreams = internalStreams.Select(s => GetMediaStream(isAudio, s, data.Format))
+ info.MediaStreams = internalStreams.Select(s => GetMediaStream(isAudio, s, data.Format, internalFrames))
.Where(i => i is not null)
// Drop subtitle streams if we don't know the codec because it will just cause failures if we don't know how to handle them
.Where(i => i.Type != MediaStreamType.Subtitle || !string.IsNullOrWhiteSpace(i.Codec))
@@ -531,42 +533,44 @@ namespace MediaBrowser.MediaEncoding.Probing
private void ProcessPairs(string key, List<NameValuePair> pairs, MediaInfo info)
{
List<BaseItemPerson> peoples = new List<BaseItemPerson>();
+ var distinctPairs = pairs.Select(p => p.Value)
+ .Where(i => !string.IsNullOrWhiteSpace(i))
+ .Trimmed()
+ .Distinct(StringComparer.OrdinalIgnoreCase);
+
if (string.Equals(key, "studio", StringComparison.OrdinalIgnoreCase))
{
- info.Studios = pairs.Select(p => p.Value)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToArray();
+ info.Studios = distinctPairs.ToArray();
}
else if (string.Equals(key, "screenwriters", StringComparison.OrdinalIgnoreCase))
{
- foreach (var pair in pairs)
+ foreach (var pair in distinctPairs)
{
peoples.Add(new BaseItemPerson
{
- Name = pair.Value,
+ Name = pair,
Type = PersonKind.Writer
});
}
}
else if (string.Equals(key, "producers", StringComparison.OrdinalIgnoreCase))
{
- foreach (var pair in pairs)
+ foreach (var pair in distinctPairs)
{
peoples.Add(new BaseItemPerson
{
- Name = pair.Value,
+ Name = pair,
Type = PersonKind.Producer
});
}
}
else if (string.Equals(key, "directors", StringComparison.OrdinalIgnoreCase))
{
- foreach (var pair in pairs)
+ foreach (var pair in distinctPairs)
{
peoples.Add(new BaseItemPerson
{
- Name = pair.Value,
+ Name = pair,
Type = PersonKind.Director
});
}
@@ -591,10 +595,10 @@ namespace MediaBrowser.MediaEncoding.Probing
switch (reader.Name)
{
case "key":
- name = reader.ReadElementContentAsString();
+ name = reader.ReadNormalizedString();
break;
case "string":
- value = reader.ReadElementContentAsString();
+ value = reader.ReadNormalizedString();
break;
default:
reader.Skip();
@@ -607,8 +611,8 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- if (string.IsNullOrWhiteSpace(name)
- || string.IsNullOrWhiteSpace(value))
+ if (string.IsNullOrEmpty(name)
+ || string.IsNullOrEmpty(value))
{
return null;
}
@@ -682,8 +686,9 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <param name="isAudio">if set to <c>true</c> [is info].</param>
/// <param name="streamInfo">The stream info.</param>
/// <param name="formatInfo">The format info.</param>
+ /// <param name="frameInfoList">The frame info.</param>
/// <returns>MediaStream.</returns>
- private MediaStream GetMediaStream(bool isAudio, MediaStreamInfo streamInfo, MediaFormatInfo formatInfo)
+ private MediaStream GetMediaStream(bool isAudio, MediaStreamInfo streamInfo, MediaFormatInfo formatInfo, IReadOnlyList<MediaFrameInfo> frameInfoList)
{
// These are mp4 chapters
if (string.Equals(streamInfo.CodecName, "mov_text", StringComparison.OrdinalIgnoreCase))
@@ -901,6 +906,15 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
}
+
+ var frameInfo = frameInfoList?.FirstOrDefault(i => i.StreamIndex == stream.Index);
+ if (frameInfo?.SideDataList != null)
+ {
+ if (frameInfo.SideDataList.Any(data => string.Equals(data.SideDataType, "HDR Dynamic Metadata SMPTE2094-40 (HDR10+)", StringComparison.OrdinalIgnoreCase)))
+ {
+ stream.Hdr10PlusPresentFlag = true;
+ }
+ }
}
else if (streamInfo.CodecType == CodecType.Data)
{
@@ -951,7 +965,7 @@ namespace MediaBrowser.MediaEncoding.Probing
// Get average bitrate info from tag "NUMBER_OF_BYTES" and "DURATION" if possible.
var durationInSeconds = GetRuntimeSecondsFromTags(streamInfo);
var bytes = GetNumberOfBytesFromTags(streamInfo);
- if (durationInSeconds is not null && bytes is not null)
+ if (durationInSeconds is not null && durationInSeconds.Value >= 1 && bytes is not null)
{
bps = Convert.ToInt32(bytes * 8 / durationInSeconds, CultureInfo.InvariantCulture);
if (bps > 0)
@@ -1453,7 +1467,7 @@ namespace MediaBrowser.MediaEncoding.Probing
var genres = new List<string>(info.Genres);
foreach (var genre in Split(genreVal, true))
{
- if (string.IsNullOrWhiteSpace(genre))
+ if (string.IsNullOrEmpty(genre))
{
continue;
}