mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-08 00:39:25 +01:00
Merge branch 'master' into trickplay
This commit is contained in:
@@ -49,6 +49,7 @@ public class EncodingOptions
|
||||
EnableIntelLowPowerHevcHwEncoder = false;
|
||||
EnableHardwareEncoding = true;
|
||||
AllowHevcEncoding = false;
|
||||
AllowAv1Encoding = false;
|
||||
AllowMjpegEncoding = false;
|
||||
EnableSubtitleExtraction = true;
|
||||
AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" };
|
||||
@@ -250,6 +251,11 @@ public class EncodingOptions
|
||||
/// </summary>
|
||||
public bool AllowHevcEncoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether AV1 encoding is enabled.
|
||||
/// </summary>
|
||||
public bool AllowAv1Encoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether MJPEG encoding is enabled.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,14 +1,38 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
/// <summary>
|
||||
/// The condition processor.
|
||||
/// </summary>
|
||||
public static class ConditionProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if a video condition is satisfied.
|
||||
/// </summary>
|
||||
/// <param name="condition">The <see cref="ProfileCondition"/>.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
/// <param name="videoBitDepth">The bit depth.</param>
|
||||
/// <param name="videoBitrate">The bitrate.</param>
|
||||
/// <param name="videoProfile">The video profile.</param>
|
||||
/// <param name="videoRangeType">The <see cref="VideoRangeType"/>.</param>
|
||||
/// <param name="videoLevel">The video level.</param>
|
||||
/// <param name="videoFramerate">The framerate.</param>
|
||||
/// <param name="packetLength">The packet length.</param>
|
||||
/// <param name="timestamp">The <see cref="TransportStreamTimestamp"/>.</param>
|
||||
/// <param name="isAnamorphic">A value indicating whether tthe video is anamorphic.</param>
|
||||
/// <param name="isInterlaced">A value indicating whether tthe video is interlaced.</param>
|
||||
/// <param name="refFrames">The reference frames.</param>
|
||||
/// <param name="numVideoStreams">The number of video streams.</param>
|
||||
/// <param name="numAudioStreams">The number of audio streams.</param>
|
||||
/// <param name="videoCodecTag">The video codec tag.</param>
|
||||
/// <param name="isAvc">A value indicating whether the video is AVC.</param>
|
||||
/// <returns><b>True</b> if the condition is satisfied.</returns>
|
||||
public static bool IsVideoConditionSatisfied(
|
||||
ProfileCondition condition,
|
||||
int? width,
|
||||
@@ -16,7 +40,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
int? videoBitDepth,
|
||||
int? videoBitrate,
|
||||
string? videoProfile,
|
||||
string? videoRangeType,
|
||||
VideoRangeType? videoRangeType,
|
||||
double? videoLevel,
|
||||
float? videoFramerate,
|
||||
int? packetLength,
|
||||
@@ -70,6 +94,13 @@ namespace MediaBrowser.Model.Dlna
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a image condition is satisfied.
|
||||
/// </summary>
|
||||
/// <param name="condition">The <see cref="ProfileCondition"/>.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
/// <returns><b>True</b> if the condition is satisfied.</returns>
|
||||
public static bool IsImageConditionSatisfied(ProfileCondition condition, int? width, int? height)
|
||||
{
|
||||
switch (condition.Property)
|
||||
@@ -83,6 +114,15 @@ namespace MediaBrowser.Model.Dlna
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an audio condition is satisfied.
|
||||
/// </summary>
|
||||
/// <param name="condition">The <see cref="ProfileCondition"/>.</param>
|
||||
/// <param name="audioChannels">The channel count.</param>
|
||||
/// <param name="audioBitrate">The bitrate.</param>
|
||||
/// <param name="audioSampleRate">The sample rate.</param>
|
||||
/// <param name="audioBitDepth">The bit depth.</param>
|
||||
/// <returns><b>True</b> if the condition is satisfied.</returns>
|
||||
public static bool IsAudioConditionSatisfied(ProfileCondition condition, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth)
|
||||
{
|
||||
switch (condition.Property)
|
||||
@@ -100,6 +140,17 @@ namespace MediaBrowser.Model.Dlna
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an audio condition is satisfied for a video.
|
||||
/// </summary>
|
||||
/// <param name="condition">The <see cref="ProfileCondition"/>.</param>
|
||||
/// <param name="audioChannels">The channel count.</param>
|
||||
/// <param name="audioBitrate">The bitrate.</param>
|
||||
/// <param name="audioSampleRate">The sample rate.</param>
|
||||
/// <param name="audioBitDepth">The bit depth.</param>
|
||||
/// <param name="audioProfile">The profile.</param>
|
||||
/// <param name="isSecondaryTrack">A value indicating whether the audio is a secondary track.</param>
|
||||
/// <returns><b>True</b> if the condition is satisfied.</returns>
|
||||
public static bool IsVideoAudioConditionSatisfied(
|
||||
ProfileCondition condition,
|
||||
int? audioChannels,
|
||||
@@ -281,5 +332,41 @@ namespace MediaBrowser.Model.Dlna
|
||||
throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsConditionSatisfied(ProfileCondition condition, VideoRangeType? currentValue)
|
||||
{
|
||||
if (!currentValue.HasValue || currentValue.Equals(VideoRangeType.Unknown))
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
var conditionType = condition.Condition;
|
||||
if (conditionType == ProfileConditionType.EqualsAny)
|
||||
{
|
||||
foreach (var singleConditionString in condition.Value.AsSpan().Split('|'))
|
||||
{
|
||||
if (Enum.TryParse(singleConditionString, true, out VideoRangeType conditionValue)
|
||||
&& conditionValue.Equals(currentValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Enum.TryParse(condition.Value, true, out VideoRangeType expected))
|
||||
{
|
||||
return conditionType switch
|
||||
{
|
||||
ProfileConditionType.Equals => currentValue.Value == expected,
|
||||
ProfileConditionType.NotEquals => currentValue.Value != expected,
|
||||
_ => throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition)
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
@@ -128,7 +129,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
bool isDirectStream,
|
||||
long? runtimeTicks,
|
||||
string videoProfile,
|
||||
string videoRangeType,
|
||||
VideoRangeType videoRangeType,
|
||||
double? videoLevel,
|
||||
float? videoFramerate,
|
||||
int? packetLength,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
|
||||
@@ -445,7 +446,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
int? bitDepth,
|
||||
int? videoBitrate,
|
||||
string videoProfile,
|
||||
string videoRangeType,
|
||||
VideoRangeType videoRangeType,
|
||||
double? videoLevel,
|
||||
float? videoFramerate,
|
||||
int? packetLength,
|
||||
|
||||
@@ -73,27 +73,5 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static double GetVideoBitrateScaleFactor(string codec)
|
||||
{
|
||||
if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return .6;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int ScaleBitrate(int bitrate, string inputVideoCodec, string outputVideoCodec)
|
||||
{
|
||||
var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec);
|
||||
var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec);
|
||||
var scaleFactor = outputScaleFactor / inputScaleFactor;
|
||||
var newBitrate = scaleFactor * bitrate;
|
||||
|
||||
return Convert.ToInt32(newBitrate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
@@ -23,7 +24,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly ITranscoderSupport _transcoderSupport;
|
||||
private static readonly string[] _supportedHlsVideoCodecs = new string[] { "h264", "hevc" };
|
||||
private static readonly string[] _supportedHlsVideoCodecs = new string[] { "h264", "hevc", "av1" };
|
||||
private static readonly string[] _supportedHlsAudioCodecsTs = new string[] { "aac", "ac3", "eac3", "mp3" };
|
||||
private static readonly string[] _supportedHlsAudioCodecsMp4 = new string[] { "aac", "ac3", "eac3", "mp3", "alac", "flac", "opus", "dca", "truehd" };
|
||||
|
||||
@@ -889,7 +890,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
int? videoBitrate = videoStream?.BitRate;
|
||||
double? videoLevel = videoStream?.Level;
|
||||
string? videoProfile = videoStream?.Profile;
|
||||
string? videoRangeType = videoStream?.VideoRangeType;
|
||||
VideoRangeType? videoRangeType = videoStream?.VideoRangeType;
|
||||
float videoFramerate = videoStream is null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
|
||||
bool? isAnamorphic = videoStream?.IsAnamorphic;
|
||||
bool? isInterlaced = videoStream?.IsInterlaced;
|
||||
@@ -1144,7 +1145,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
int? videoBitrate = videoStream?.BitRate;
|
||||
double? videoLevel = videoStream?.Level;
|
||||
string? videoProfile = videoStream?.Profile;
|
||||
string? videoRangeType = videoStream?.VideoRangeType;
|
||||
VideoRangeType? videoRangeType = videoStream?.VideoRangeType;
|
||||
float videoFramerate = videoStream is null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
|
||||
bool? isAnamorphic = videoStream?.IsAnamorphic;
|
||||
bool? isInterlaced = videoStream?.IsInterlaced;
|
||||
@@ -1932,6 +1933,10 @@ namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
item.SetOption(qualifier, "rangetype", string.Join(',', values));
|
||||
}
|
||||
else if (condition.Condition == ProfileConditionType.NotEquals)
|
||||
{
|
||||
item.SetOption(qualifier, "rangetype", string.Join(',', Enum.GetNames(typeof(VideoRangeType)).Except(values)));
|
||||
}
|
||||
else if (condition.Condition == ProfileConditionType.EqualsAny)
|
||||
{
|
||||
var currentValue = item.GetOption(qualifier, "rangetype");
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
@@ -281,23 +282,24 @@ namespace MediaBrowser.Model.Dlna
|
||||
/// <summary>
|
||||
/// Gets the target video range type that will be in the output stream.
|
||||
/// </summary>
|
||||
public string TargetVideoRangeType
|
||||
public VideoRangeType TargetVideoRangeType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsDirectStream)
|
||||
{
|
||||
return TargetVideoStream?.VideoRangeType;
|
||||
return TargetVideoStream?.VideoRangeType ?? VideoRangeType.Unknown;
|
||||
}
|
||||
|
||||
var targetVideoCodecs = TargetVideoCodec;
|
||||
var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
|
||||
if (!string.IsNullOrEmpty(videoCodec))
|
||||
if (!string.IsNullOrEmpty(videoCodec)
|
||||
&& Enum.TryParse(GetOption(videoCodec, "rangetype"), true, out VideoRangeType videoRangeType))
|
||||
{
|
||||
return GetOption(videoCodec, "rangetype");
|
||||
return videoRangeType;
|
||||
}
|
||||
|
||||
return TargetVideoStream?.VideoRangeType;
|
||||
return TargetVideoStream?.VideoRangeType ?? VideoRangeType.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
@@ -148,7 +149,7 @@ namespace MediaBrowser.Model.Entities
|
||||
/// Gets the video range.
|
||||
/// </summary>
|
||||
/// <value>The video range.</value>
|
||||
public string VideoRange
|
||||
public VideoRange VideoRange
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -162,7 +163,7 @@ namespace MediaBrowser.Model.Entities
|
||||
/// Gets the video range type.
|
||||
/// </summary>
|
||||
/// <value>The video range type.</value>
|
||||
public string VideoRangeType
|
||||
public VideoRangeType VideoRangeType
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -306,9 +307,9 @@ namespace MediaBrowser.Model.Entities
|
||||
attributes.Add(Codec.ToUpperInvariant());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(VideoRange))
|
||||
if (VideoRange != VideoRange.Unknown)
|
||||
{
|
||||
attributes.Add(VideoRange.ToUpperInvariant());
|
||||
attributes.Add(VideoRange.ToString());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Title))
|
||||
@@ -677,23 +678,23 @@ namespace MediaBrowser.Model.Entities
|
||||
return true;
|
||||
}
|
||||
|
||||
public (string VideoRange, string VideoRangeType) GetVideoColorRange()
|
||||
public (VideoRange VideoRange, VideoRangeType VideoRangeType) GetVideoColorRange()
|
||||
{
|
||||
if (Type != MediaStreamType.Video)
|
||||
{
|
||||
return (null, null);
|
||||
return (VideoRange.Unknown, VideoRangeType.Unknown);
|
||||
}
|
||||
|
||||
var colorTransfer = ColorTransfer;
|
||||
|
||||
if (string.Equals(colorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ("HDR", "HDR10");
|
||||
return (VideoRange.HDR, VideoRangeType.HDR10);
|
||||
}
|
||||
|
||||
if (string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ("HDR", "HLG");
|
||||
return (VideoRange.HDR, VideoRangeType.HLG);
|
||||
}
|
||||
|
||||
var codecTag = CodecTag;
|
||||
@@ -711,10 +712,10 @@ namespace MediaBrowser.Model.Entities
|
||||
|| string.Equals(codecTag, "dvhe", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codecTag, "dav1", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ("HDR", "DOVI");
|
||||
return (VideoRange.HDR, VideoRangeType.DOVI);
|
||||
}
|
||||
|
||||
return ("SDR", "SDR");
|
||||
return (VideoRange.SDR, VideoRangeType.SDR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#pragma warning disable CS1591, CA1819
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
using Jellyfin.Data.Enums;
|
||||
using AccessSchedule = Jellyfin.Data.Entities.AccessSchedule;
|
||||
@@ -79,6 +80,7 @@ namespace MediaBrowser.Model.Users
|
||||
/// Gets or sets a value indicating whether this instance can manage collections.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
|
||||
[DefaultValue(false)]
|
||||
public bool EnableCollectionManagement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user