repo creation with initial code after cloning public repo
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="CodecProfile"/>.
|
||||
/// </summary>
|
||||
public class CodecProfile
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CodecProfile"/> class.
|
||||
/// </summary>
|
||||
public CodecProfile()
|
||||
{
|
||||
Conditions = [];
|
||||
ApplyConditions = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="CodecType"/> which this container must meet.
|
||||
/// </summary>
|
||||
[XmlAttribute("type")]
|
||||
public CodecType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of <see cref="ProfileCondition"/> which this profile must meet.
|
||||
/// </summary>
|
||||
public ProfileCondition[] Conditions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of <see cref="ProfileCondition"/> to apply if this profile is met.
|
||||
/// </summary>
|
||||
public ProfileCondition[] ApplyConditions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the codec(s) that this profile applies to.
|
||||
/// </summary>
|
||||
[XmlAttribute("codec")]
|
||||
public string? Codec { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the container(s) which this profile will be applied to.
|
||||
/// </summary>
|
||||
[XmlAttribute("container")]
|
||||
public string? Container { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sub-container(s) which this profile will be applied to.
|
||||
/// </summary>
|
||||
[XmlAttribute("subcontainer")]
|
||||
public string? SubContainer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see whether the codecs and containers contain the given parameters.
|
||||
/// </summary>
|
||||
/// <param name="codecs">The codecs to match.</param>
|
||||
/// <param name="container">The container to match.</param>
|
||||
/// <param name="useSubContainer">Consider sub-containers.</param>
|
||||
/// <returns>True if both conditions are met.</returns>
|
||||
public bool ContainsAnyCodec(IReadOnlyList<string> codecs, string? container, bool useSubContainer = false)
|
||||
{
|
||||
var containerToCheck = useSubContainer && string.Equals(Container, "hls", StringComparison.OrdinalIgnoreCase) ? SubContainer : Container;
|
||||
return ContainerHelper.ContainsContainer(containerToCheck, container) && codecs.Any(c => ContainerHelper.ContainsContainer(Codec, false, c));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see whether the codecs and containers contain the given parameters.
|
||||
/// </summary>
|
||||
/// <param name="codec">The codec to match.</param>
|
||||
/// <param name="container">The container to match.</param>
|
||||
/// <param name="useSubContainer">Consider sub-containers.</param>
|
||||
/// <returns>True if both conditions are met.</returns>
|
||||
public bool ContainsAnyCodec(string? codec, string? container, bool useSubContainer = false)
|
||||
{
|
||||
return ContainsAnyCodec(codec.AsSpan(), container, useSubContainer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see whether the codecs and containers contain the given parameters.
|
||||
/// </summary>
|
||||
/// <param name="codec">The codec to match.</param>
|
||||
/// <param name="container">The container to match.</param>
|
||||
/// <param name="useSubContainer">Consider sub-containers.</param>
|
||||
/// <returns>True if both conditions are met.</returns>
|
||||
public bool ContainsAnyCodec(ReadOnlySpan<char> codec, string? container, bool useSubContainer = false)
|
||||
{
|
||||
var containerToCheck = useSubContainer && string.Equals(Container, "hls", StringComparison.OrdinalIgnoreCase) ? SubContainer : Container;
|
||||
return ContainerHelper.ContainsContainer(containerToCheck, container) && ContainerHelper.ContainsContainer(Codec, false, codec);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum CodecType
|
||||
{
|
||||
Video = 0,
|
||||
VideoAudio = 1,
|
||||
Audio = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
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 the video is anamorphic.</param>
|
||||
/// <param name="isInterlaced">A value indicating whether the video is interlaced.</param>
|
||||
/// <param name="refFrames">The reference frames.</param>
|
||||
/// <param name="numStreams">The number of streams.</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,
|
||||
int? height,
|
||||
int? videoBitDepth,
|
||||
int? videoBitrate,
|
||||
string? videoProfile,
|
||||
VideoRangeType? videoRangeType,
|
||||
double? videoLevel,
|
||||
float? videoFramerate,
|
||||
int? packetLength,
|
||||
TransportStreamTimestamp? timestamp,
|
||||
bool? isAnamorphic,
|
||||
bool? isInterlaced,
|
||||
int? refFrames,
|
||||
int numStreams,
|
||||
int? numVideoStreams,
|
||||
int? numAudioStreams,
|
||||
string? videoCodecTag,
|
||||
bool? isAvc)
|
||||
{
|
||||
switch (condition.Property)
|
||||
{
|
||||
case ProfileConditionValue.IsInterlaced:
|
||||
return IsConditionSatisfied(condition, isInterlaced);
|
||||
case ProfileConditionValue.IsAnamorphic:
|
||||
return IsConditionSatisfied(condition, isAnamorphic);
|
||||
case ProfileConditionValue.IsAvc:
|
||||
return IsConditionSatisfied(condition, isAvc);
|
||||
case ProfileConditionValue.VideoFramerate:
|
||||
return IsConditionSatisfied(condition, videoFramerate);
|
||||
case ProfileConditionValue.VideoLevel:
|
||||
return IsConditionSatisfied(condition, videoLevel);
|
||||
case ProfileConditionValue.VideoProfile:
|
||||
return IsConditionSatisfied(condition, videoProfile);
|
||||
case ProfileConditionValue.VideoRangeType:
|
||||
return IsConditionSatisfied(condition, videoRangeType);
|
||||
case ProfileConditionValue.VideoCodecTag:
|
||||
return IsConditionSatisfied(condition, videoCodecTag);
|
||||
case ProfileConditionValue.PacketLength:
|
||||
return IsConditionSatisfied(condition, packetLength);
|
||||
case ProfileConditionValue.VideoBitDepth:
|
||||
return IsConditionSatisfied(condition, videoBitDepth);
|
||||
case ProfileConditionValue.VideoBitrate:
|
||||
return IsConditionSatisfied(condition, videoBitrate);
|
||||
case ProfileConditionValue.Height:
|
||||
return IsConditionSatisfied(condition, height);
|
||||
case ProfileConditionValue.Width:
|
||||
return IsConditionSatisfied(condition, width);
|
||||
case ProfileConditionValue.RefFrames:
|
||||
return IsConditionSatisfied(condition, refFrames);
|
||||
case ProfileConditionValue.NumStreams:
|
||||
return IsConditionSatisfied(condition, numStreams);
|
||||
case ProfileConditionValue.NumAudioStreams:
|
||||
return IsConditionSatisfied(condition, numAudioStreams);
|
||||
case ProfileConditionValue.NumVideoStreams:
|
||||
return IsConditionSatisfied(condition, numVideoStreams);
|
||||
case ProfileConditionValue.VideoTimestamp:
|
||||
return IsConditionSatisfied(condition, timestamp);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
case ProfileConditionValue.Height:
|
||||
return IsConditionSatisfied(condition, height);
|
||||
case ProfileConditionValue.Width:
|
||||
return IsConditionSatisfied(condition, width);
|
||||
default:
|
||||
throw new ArgumentException("Unexpected condition on image file: " + condition.Property);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
case ProfileConditionValue.AudioBitrate:
|
||||
return IsConditionSatisfied(condition, audioBitrate);
|
||||
case ProfileConditionValue.AudioChannels:
|
||||
return IsConditionSatisfied(condition, audioChannels);
|
||||
case ProfileConditionValue.AudioSampleRate:
|
||||
return IsConditionSatisfied(condition, audioSampleRate);
|
||||
case ProfileConditionValue.AudioBitDepth:
|
||||
return IsConditionSatisfied(condition, audioBitDepth);
|
||||
default:
|
||||
throw new ArgumentException("Unexpected condition on audio file: " + condition.Property);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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,
|
||||
int? audioBitrate,
|
||||
int? audioSampleRate,
|
||||
int? audioBitDepth,
|
||||
string? audioProfile,
|
||||
bool? isSecondaryTrack)
|
||||
{
|
||||
switch (condition.Property)
|
||||
{
|
||||
case ProfileConditionValue.AudioProfile:
|
||||
return IsConditionSatisfied(condition, audioProfile);
|
||||
case ProfileConditionValue.AudioBitrate:
|
||||
return IsConditionSatisfied(condition, audioBitrate);
|
||||
case ProfileConditionValue.AudioChannels:
|
||||
return IsConditionSatisfied(condition, audioChannels);
|
||||
case ProfileConditionValue.IsSecondaryAudio:
|
||||
return IsConditionSatisfied(condition, isSecondaryTrack);
|
||||
case ProfileConditionValue.AudioSampleRate:
|
||||
return IsConditionSatisfied(condition, audioSampleRate);
|
||||
case ProfileConditionValue.AudioBitDepth:
|
||||
return IsConditionSatisfied(condition, audioBitDepth);
|
||||
default:
|
||||
throw new ArgumentException("Unexpected condition on audio file: " + condition.Property);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsConditionSatisfied(ProfileCondition condition, int? currentValue)
|
||||
{
|
||||
if (!currentValue.HasValue)
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
var conditionType = condition.Condition;
|
||||
if (condition.Condition == ProfileConditionType.EqualsAny)
|
||||
{
|
||||
foreach (var singleConditionString in condition.Value.AsSpan().Split('|'))
|
||||
{
|
||||
if (int.TryParse(singleConditionString, NumberStyles.Integer, CultureInfo.InvariantCulture, out int conditionValue)
|
||||
&& conditionValue.Equals(currentValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (int.TryParse(condition.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var expected))
|
||||
{
|
||||
switch (conditionType)
|
||||
{
|
||||
case ProfileConditionType.Equals:
|
||||
return currentValue.Value.Equals(expected);
|
||||
case ProfileConditionType.GreaterThanEqual:
|
||||
return currentValue.Value >= expected;
|
||||
case ProfileConditionType.LessThanEqual:
|
||||
return currentValue.Value <= expected;
|
||||
case ProfileConditionType.NotEquals:
|
||||
return !currentValue.Value.Equals(expected);
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsConditionSatisfied(ProfileCondition condition, string? currentValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(currentValue))
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
string expected = condition.Value;
|
||||
|
||||
switch (condition.Condition)
|
||||
{
|
||||
case ProfileConditionType.EqualsAny:
|
||||
return expected.Split('|').Contains(currentValue, StringComparison.OrdinalIgnoreCase);
|
||||
case ProfileConditionType.Equals:
|
||||
return string.Equals(currentValue, expected, StringComparison.OrdinalIgnoreCase);
|
||||
case ProfileConditionType.NotEquals:
|
||||
return !string.Equals(currentValue, expected, StringComparison.OrdinalIgnoreCase);
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsConditionSatisfied(ProfileCondition condition, bool? currentValue)
|
||||
{
|
||||
if (!currentValue.HasValue)
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
if (bool.TryParse(condition.Value, out var expected))
|
||||
{
|
||||
switch (condition.Condition)
|
||||
{
|
||||
case ProfileConditionType.Equals:
|
||||
return currentValue.Value == expected;
|
||||
case ProfileConditionType.NotEquals:
|
||||
return currentValue.Value != expected;
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsConditionSatisfied(ProfileCondition condition, double? currentValue)
|
||||
{
|
||||
if (!currentValue.HasValue)
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
var conditionType = condition.Condition;
|
||||
if (condition.Condition == ProfileConditionType.EqualsAny)
|
||||
{
|
||||
foreach (var singleConditionString in condition.Value.AsSpan().Split('|'))
|
||||
{
|
||||
if (double.TryParse(singleConditionString, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double conditionValue)
|
||||
&& conditionValue.Equals(currentValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (double.TryParse(condition.Value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var expected))
|
||||
{
|
||||
switch (conditionType)
|
||||
{
|
||||
case ProfileConditionType.Equals:
|
||||
return currentValue.Value.Equals(expected);
|
||||
case ProfileConditionType.GreaterThanEqual:
|
||||
return currentValue.Value >= expected;
|
||||
case ProfileConditionType.LessThanEqual:
|
||||
return currentValue.Value <= expected;
|
||||
case ProfileConditionType.NotEquals:
|
||||
return !currentValue.Value.Equals(expected);
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsConditionSatisfied(ProfileCondition condition, TransportStreamTimestamp? timestamp)
|
||||
{
|
||||
if (!timestamp.HasValue)
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
var expected = (TransportStreamTimestamp)Enum.Parse(typeof(TransportStreamTimestamp), condition.Value, true);
|
||||
|
||||
switch (condition.Condition)
|
||||
{
|
||||
case ProfileConditionType.Equals:
|
||||
return timestamp == expected;
|
||||
case ProfileConditionType.NotEquals:
|
||||
return timestamp != expected;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
||||
// Special case: HDR10 also satisfies if the video is HDR10Plus
|
||||
if (currentValue.Value == VideoRangeType.HDR10Plus)
|
||||
{
|
||||
if (IsConditionSatisfied(condition, VideoRangeType.HDR10))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma warning disable CA1819 // Properties should not return arrays
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="ContainerProfile"/>.
|
||||
/// </summary>
|
||||
public class ContainerProfile
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="DlnaProfileType"/> which this container must meet.
|
||||
/// </summary>
|
||||
[XmlAttribute("type")]
|
||||
public DlnaProfileType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of <see cref="ProfileCondition"/> which this container will be applied to.
|
||||
/// </summary>
|
||||
public ProfileCondition[] Conditions { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the container(s) which this container must meet.
|
||||
/// </summary>
|
||||
[XmlAttribute("container")]
|
||||
public string? Container { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sub container(s) which this container must meet.
|
||||
/// </summary>
|
||||
[XmlAttribute("subcontainer")]
|
||||
public string? SubContainer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if an item in <paramref name="container"/> appears in the <see cref="Container"/> property.
|
||||
/// </summary>
|
||||
/// <param name="container">The item to match.</param>
|
||||
/// <param name="useSubContainer">Consider subcontainers.</param>
|
||||
/// <returns>The result of the operation.</returns>
|
||||
public bool ContainsContainer(ReadOnlySpan<char> container, bool useSubContainer = false)
|
||||
{
|
||||
var containerToCheck = useSubContainer && string.Equals(Container, "hls", StringComparison.OrdinalIgnoreCase) ? SubContainer : Container;
|
||||
return ContainerHelper.ContainsContainer(containerToCheck, container);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma warning disable CA1819 // Properties should not return arrays
|
||||
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna;
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="DeviceProfile" /> represents a set of metadata which determines which content a certain device is able to play.
|
||||
/// <br/>
|
||||
/// Specifically, it defines the supported <see cref="ContainerProfiles">containers</see> and
|
||||
/// <see cref="CodecProfiles">codecs</see> (video and/or audio, including codec profiles and levels)
|
||||
/// the device is able to direct play (without transcoding or remuxing),
|
||||
/// as well as which <see cref="TranscodingProfiles">containers/codecs to transcode to</see> in case it isn't.
|
||||
/// </summary>
|
||||
public class DeviceProfile
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of this device profile. User profiles must have a unique name.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unique internal identifier.
|
||||
/// </summary>
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum allowed bitrate for all streamed content.
|
||||
/// </summary>
|
||||
public int? MaxStreamingBitrate { get; set; } = 8000000;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum allowed bitrate for statically streamed content (= direct played files).
|
||||
/// </summary>
|
||||
public int? MaxStaticBitrate { get; set; } = 8000000;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum allowed bitrate for transcoded music streams.
|
||||
/// </summary>
|
||||
public int? MusicStreamingTranscodingBitrate { get; set; } = 128000;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum allowed bitrate for statically streamed (= direct played) music files.
|
||||
/// </summary>
|
||||
public int? MaxStaticMusicBitrate { get; set; } = 8000000;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the direct play profiles.
|
||||
/// </summary>
|
||||
public DirectPlayProfile[] DirectPlayProfiles { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transcoding profiles.
|
||||
/// </summary>
|
||||
public TranscodingProfile[] TranscodingProfiles { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the container profiles. Failing to meet these optional conditions causes transcoding to occur.
|
||||
/// </summary>
|
||||
public ContainerProfile[] ContainerProfiles { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the codec profiles.
|
||||
/// </summary>
|
||||
public CodecProfile[] CodecProfiles { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the subtitle profiles.
|
||||
/// </summary>
|
||||
public SubtitleProfile[] SubtitleProfiles { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="DirectPlayProfile"/>.
|
||||
/// </summary>
|
||||
public class DirectPlayProfile
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the container.
|
||||
/// </summary>
|
||||
[XmlAttribute("container")]
|
||||
public string Container { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio codec.
|
||||
/// </summary>
|
||||
[XmlAttribute("audioCodec")]
|
||||
public string? AudioCodec { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the video codec.
|
||||
/// </summary>
|
||||
[XmlAttribute("videoCodec")]
|
||||
public string? VideoCodec { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Dlna profile type.
|
||||
/// </summary>
|
||||
[XmlAttribute("type")]
|
||||
public DlnaProfileType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the <see cref="Container"/> supports the <paramref name="container"/>.
|
||||
/// </summary>
|
||||
/// <param name="container">The container to match against.</param>
|
||||
/// <returns>True if supported.</returns>
|
||||
public bool SupportsContainer(string? container)
|
||||
{
|
||||
return ContainerHelper.ContainsContainer(Container, container);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the <see cref="VideoCodec"/> supports the <paramref name="codec"/>.
|
||||
/// </summary>
|
||||
/// <param name="codec">The codec to match against.</param>
|
||||
/// <returns>True if supported.</returns>
|
||||
public bool SupportsVideoCodec(string? codec)
|
||||
{
|
||||
return Type == DlnaProfileType.Video && ContainerHelper.ContainsContainer(VideoCodec, codec);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the <see cref="AudioCodec"/> supports the <paramref name="codec"/>.
|
||||
/// </summary>
|
||||
/// <param name="codec">The codec to match against.</param>
|
||||
/// <returns>True if supported.</returns>
|
||||
public bool SupportsAudioCodec(string? codec)
|
||||
{
|
||||
// Video profiles can have audio codec restrictions too, therefore include Video as valid type.
|
||||
return (Type == DlnaProfileType.Audio || Type == DlnaProfileType.Video) && ContainerHelper.ContainsContainer(AudioCodec, codec);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum DlnaProfileType
|
||||
{
|
||||
Audio = 0,
|
||||
Video = 1,
|
||||
Photo = 2,
|
||||
Subtitle = 3,
|
||||
Lyric = 4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum EncodingContext
|
||||
{
|
||||
Streaming = 0,
|
||||
Static = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public interface ITranscoderSupport
|
||||
{
|
||||
bool CanEncodeToAudioCodec(string codec);
|
||||
|
||||
bool CanEncodeToSubtitleCodec(string codec);
|
||||
|
||||
bool CanExtractSubtitles(string codec);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using MediaBrowser.Model.Dto;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
/// <summary>
|
||||
/// Class MediaOptions.
|
||||
/// </summary>
|
||||
public class MediaOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MediaOptions"/> class.
|
||||
/// </summary>
|
||||
public MediaOptions()
|
||||
{
|
||||
Context = EncodingContext.Streaming;
|
||||
|
||||
EnableDirectPlay = true;
|
||||
EnableDirectStream = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether direct playback is allowed.
|
||||
/// </summary>
|
||||
public bool EnableDirectPlay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether direct streaming is allowed.
|
||||
/// </summary>
|
||||
public bool EnableDirectStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether direct playback is forced.
|
||||
/// </summary>
|
||||
public bool ForceDirectPlay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether direct streaming is forced.
|
||||
/// </summary>
|
||||
public bool ForceDirectStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether audio stream copy is allowed.
|
||||
/// </summary>
|
||||
public bool AllowAudioStreamCopy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether video stream copy is allowed.
|
||||
/// </summary>
|
||||
public bool AllowVideoStreamCopy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether always burn in subtitles when transcoding.
|
||||
/// </summary>
|
||||
public bool AlwaysBurnInSubtitleWhenTranscoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item id.
|
||||
/// </summary>
|
||||
public Guid ItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the media sources.
|
||||
/// </summary>
|
||||
public MediaSourceInfo[] MediaSources { get; set; } = Array.Empty<MediaSourceInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the device profile.
|
||||
/// </summary>
|
||||
public required DeviceProfile Profile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a media source id. Optional. Only needed if a specific AudioStreamIndex or SubtitleStreamIndex are requested.
|
||||
/// </summary>
|
||||
public string? MediaSourceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the device id.
|
||||
/// </summary>
|
||||
public string? DeviceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an override of supported number of audio channels
|
||||
/// Example: DeviceProfile supports five channel, but user only has stereo speakers.
|
||||
/// </summary>
|
||||
public int? MaxAudioChannels { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application's configured maximum bitrate.
|
||||
/// </summary>
|
||||
public int? MaxBitrate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the context.
|
||||
/// </summary>
|
||||
/// <value>The context.</value>
|
||||
public EncodingContext Context { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio transcoding bitrate.
|
||||
/// </summary>
|
||||
/// <value>The audio transcoding bitrate.</value>
|
||||
public int? AudioTranscodingBitrate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an override for the audio stream index.
|
||||
/// </summary>
|
||||
public int? AudioStreamIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an override for the subtitle stream index.
|
||||
/// </summary>
|
||||
public int? SubtitleStreamIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum bitrate.
|
||||
/// </summary>
|
||||
/// <param name="isAudio">Whether or not this is audio.</param>
|
||||
/// <returns>System.Nullable<System.Int32>.</returns>
|
||||
public int? GetMaxBitrate(bool isAudio)
|
||||
{
|
||||
if (MaxBitrate.HasValue)
|
||||
{
|
||||
return MaxBitrate;
|
||||
}
|
||||
|
||||
if (Profile is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Context == EncodingContext.Static)
|
||||
{
|
||||
if (isAudio && Profile.MaxStaticMusicBitrate.HasValue)
|
||||
{
|
||||
return Profile.MaxStaticMusicBitrate;
|
||||
}
|
||||
|
||||
return Profile.MaxStaticBitrate;
|
||||
}
|
||||
|
||||
return Profile.MaxStreamingBitrate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum PlaybackErrorCode
|
||||
{
|
||||
NotAllowed = 0,
|
||||
NoCompatibleStream = 1,
|
||||
RateLimitExceeded = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#nullable disable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ProfileCondition
|
||||
{
|
||||
public ProfileCondition()
|
||||
{
|
||||
IsRequired = true;
|
||||
}
|
||||
|
||||
public ProfileCondition(ProfileConditionType condition, ProfileConditionValue property, string value)
|
||||
: this(condition, property, value, false)
|
||||
{
|
||||
}
|
||||
|
||||
public ProfileCondition(ProfileConditionType condition, ProfileConditionValue property, string value, bool isRequired)
|
||||
{
|
||||
Condition = condition;
|
||||
Property = property;
|
||||
Value = value;
|
||||
IsRequired = isRequired;
|
||||
}
|
||||
|
||||
[XmlAttribute("condition")]
|
||||
public ProfileConditionType Condition { get; set; }
|
||||
|
||||
[XmlAttribute("property")]
|
||||
public ProfileConditionValue Property { get; set; }
|
||||
|
||||
[XmlAttribute("value")]
|
||||
public string Value { get; set; }
|
||||
|
||||
[XmlAttribute("isRequired")]
|
||||
public bool IsRequired { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum ProfileConditionType
|
||||
{
|
||||
Equals = 0,
|
||||
NotEquals = 1,
|
||||
LessThanEqual = 2,
|
||||
GreaterThanEqual = 3,
|
||||
EqualsAny = 4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum ProfileConditionValue
|
||||
{
|
||||
AudioChannels = 0,
|
||||
AudioBitrate = 1,
|
||||
AudioProfile = 2,
|
||||
Width = 3,
|
||||
Height = 4,
|
||||
Has64BitOffsets = 5,
|
||||
PacketLength = 6,
|
||||
VideoBitDepth = 7,
|
||||
VideoBitrate = 8,
|
||||
VideoFramerate = 9,
|
||||
VideoLevel = 10,
|
||||
VideoProfile = 11,
|
||||
VideoTimestamp = 12,
|
||||
IsAnamorphic = 13,
|
||||
RefFrames = 14,
|
||||
NumAudioStreams = 16,
|
||||
NumVideoStreams = 17,
|
||||
IsSecondaryAudio = 18,
|
||||
VideoCodecTag = 19,
|
||||
IsAvc = 20,
|
||||
IsInterlaced = 21,
|
||||
AudioSampleRate = 22,
|
||||
AudioBitDepth = 23,
|
||||
VideoRangeType = 24,
|
||||
NumStreams = 25
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ResolutionConfiguration
|
||||
{
|
||||
public ResolutionConfiguration(int maxWidth, int maxBitrate)
|
||||
{
|
||||
MaxWidth = maxWidth;
|
||||
MaxBitrate = maxBitrate;
|
||||
}
|
||||
|
||||
public int MaxWidth { get; set; }
|
||||
|
||||
public int MaxBitrate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#nullable disable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public static class ResolutionNormalizer
|
||||
{
|
||||
// Please note: all bitrate here are in the scale of SDR h264 bitrate at 30fps
|
||||
private static readonly ResolutionConfiguration[] _configurations =
|
||||
[
|
||||
new ResolutionConfiguration(416, 365000),
|
||||
new ResolutionConfiguration(640, 730000),
|
||||
new ResolutionConfiguration(768, 1100000),
|
||||
new ResolutionConfiguration(960, 3000000),
|
||||
new ResolutionConfiguration(1280, 6000000),
|
||||
new ResolutionConfiguration(1920, 13500000),
|
||||
new ResolutionConfiguration(2560, 28000000),
|
||||
new ResolutionConfiguration(3840, 50000000)
|
||||
];
|
||||
|
||||
public static ResolutionOptions Normalize(
|
||||
int? inputBitrate,
|
||||
int outputBitrate,
|
||||
int h264EquivalentOutputBitrate,
|
||||
int? maxWidth,
|
||||
int? maxHeight,
|
||||
float? targetFps,
|
||||
bool isHdr = false) // We are not doing HDR transcoding for now, leave for future use
|
||||
{
|
||||
// If the bitrate isn't changing, then don't downscale the resolution
|
||||
if (inputBitrate.HasValue && outputBitrate >= inputBitrate.Value)
|
||||
{
|
||||
if (maxWidth.HasValue || maxHeight.HasValue)
|
||||
{
|
||||
return new ResolutionOptions
|
||||
{
|
||||
MaxWidth = maxWidth,
|
||||
MaxHeight = maxHeight
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Our reference bitrate is based on SDR h264 at 30fps
|
||||
var referenceFps = targetFps ?? 30.0f;
|
||||
var referenceScale = referenceFps <= 30.0f
|
||||
? 30.0f / referenceFps
|
||||
: 1.0f / MathF.Sqrt(referenceFps / 30.0f);
|
||||
var referenceBitrate = h264EquivalentOutputBitrate * referenceScale;
|
||||
|
||||
if (isHdr)
|
||||
{
|
||||
referenceBitrate *= 0.8f;
|
||||
}
|
||||
|
||||
var resolutionConfig = GetResolutionConfiguration(Convert.ToInt32(referenceBitrate));
|
||||
|
||||
if (resolutionConfig is null)
|
||||
{
|
||||
return new ResolutionOptions { MaxWidth = maxWidth, MaxHeight = maxHeight };
|
||||
}
|
||||
|
||||
var originWidthValue = maxWidth;
|
||||
|
||||
maxWidth = Math.Min(resolutionConfig.MaxWidth, maxWidth ?? resolutionConfig.MaxWidth);
|
||||
if (!originWidthValue.HasValue || originWidthValue.Value != maxWidth.Value)
|
||||
{
|
||||
maxHeight = null;
|
||||
}
|
||||
|
||||
return new ResolutionOptions
|
||||
{
|
||||
MaxWidth = maxWidth,
|
||||
MaxHeight = maxHeight
|
||||
};
|
||||
}
|
||||
|
||||
private static ResolutionConfiguration GetResolutionConfiguration(int outputBitrate)
|
||||
{
|
||||
return _configurations.FirstOrDefault(config => outputBitrate <= config.MaxBitrate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ResolutionOptions
|
||||
{
|
||||
public int? MaxWidth { get; set; }
|
||||
|
||||
public int? MaxHeight { get; set; }
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
/// <summary>
|
||||
/// Delivery method to use during playback of a specific subtitle format.
|
||||
/// </summary>
|
||||
public enum SubtitleDeliveryMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Burn the subtitles in the video track.
|
||||
/// </summary>
|
||||
Encode = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Embed the subtitles in the file or stream.
|
||||
/// </summary>
|
||||
Embed = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Serve the subtitles as an external file.
|
||||
/// </summary>
|
||||
External = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Serve the subtitles as a separate HLS stream.
|
||||
/// </summary>
|
||||
Hls = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Drop the subtitle.
|
||||
/// </summary>
|
||||
Drop = 4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna;
|
||||
|
||||
/// <summary>
|
||||
/// A class for subtitle profile information.
|
||||
/// </summary>
|
||||
public class SubtitleProfile
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the format.
|
||||
/// </summary>
|
||||
[XmlAttribute("format")]
|
||||
public string Format { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delivery method.
|
||||
/// </summary>
|
||||
[XmlAttribute("method")]
|
||||
public SubtitleDeliveryMethod Method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DIDL mode.
|
||||
/// </summary>
|
||||
[XmlAttribute("didlMode")]
|
||||
public string DidlMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the language.
|
||||
/// </summary>
|
||||
[XmlAttribute("language")]
|
||||
public string Language { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the container.
|
||||
/// </summary>
|
||||
[XmlAttribute("container")]
|
||||
public string Container { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a language is supported.
|
||||
/// </summary>
|
||||
/// <param name="subLanguage">The language to check for support.</param>
|
||||
/// <returns><c>true</c> if supported.</returns>
|
||||
public bool SupportsLanguage(string subLanguage)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Language))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(subLanguage))
|
||||
{
|
||||
subLanguage = "und";
|
||||
}
|
||||
|
||||
return ContainerHelper.ContainsContainer(Language, subLanguage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#nullable disable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class SubtitleStreamInfo
|
||||
{
|
||||
public string Url { get; set; }
|
||||
|
||||
public string Language { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool IsForced { get; set; }
|
||||
|
||||
public string Format { get; set; }
|
||||
|
||||
public string DisplayTitle { get; set; }
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public SubtitleDeliveryMethod DeliveryMethod { get; set; }
|
||||
|
||||
public bool IsExternalUrl { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum TranscodeSeekInfo
|
||||
{
|
||||
Auto = 0,
|
||||
Bytes = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
using Jellyfin.Data.Enums;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna;
|
||||
|
||||
/// <summary>
|
||||
/// A class for transcoding profile information.
|
||||
/// Note for client developers: Conditions defined in <see cref="CodecProfile"/> has higher priority and can override values defined here.
|
||||
/// </summary>
|
||||
public class TranscodingProfile
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TranscodingProfile" /> class.
|
||||
/// </summary>
|
||||
public TranscodingProfile()
|
||||
{
|
||||
Conditions = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TranscodingProfile" /> class copying the values from another instance.
|
||||
/// </summary>
|
||||
/// <param name="other">Another instance of <see cref="TranscodingProfile" /> to be copied.</param>
|
||||
public TranscodingProfile(TranscodingProfile other)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(other);
|
||||
|
||||
Container = other.Container;
|
||||
Type = other.Type;
|
||||
VideoCodec = other.VideoCodec;
|
||||
AudioCodec = other.AudioCodec;
|
||||
Protocol = other.Protocol;
|
||||
EstimateContentLength = other.EstimateContentLength;
|
||||
EnableMpegtsM2TsMode = other.EnableMpegtsM2TsMode;
|
||||
TranscodeSeekInfo = other.TranscodeSeekInfo;
|
||||
CopyTimestamps = other.CopyTimestamps;
|
||||
Context = other.Context;
|
||||
EnableSubtitlesInManifest = other.EnableSubtitlesInManifest;
|
||||
MaxAudioChannels = other.MaxAudioChannels;
|
||||
MinSegments = other.MinSegments;
|
||||
SegmentLength = other.SegmentLength;
|
||||
Conditions = other.Conditions;
|
||||
EnableAudioVbrEncoding = other.EnableAudioVbrEncoding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the container.
|
||||
/// </summary>
|
||||
[XmlAttribute("container")]
|
||||
public string Container { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DLNA profile type.
|
||||
/// </summary>
|
||||
[XmlAttribute("type")]
|
||||
public DlnaProfileType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the video codec.
|
||||
/// </summary>
|
||||
[XmlAttribute("videoCodec")]
|
||||
public string VideoCodec { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio codec.
|
||||
/// </summary>
|
||||
[XmlAttribute("audioCodec")]
|
||||
public string AudioCodec { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the protocol.
|
||||
/// </summary>
|
||||
[XmlAttribute("protocol")]
|
||||
public MediaStreamProtocol Protocol { get; set; } = MediaStreamProtocol.http;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the content length should be estimated.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[XmlAttribute("estimateContentLength")]
|
||||
public bool EstimateContentLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether M2TS mode is enabled.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[XmlAttribute("enableMpegtsM2TsMode")]
|
||||
public bool EnableMpegtsM2TsMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transcoding seek info mode.
|
||||
/// </summary>
|
||||
[DefaultValue(TranscodeSeekInfo.Auto)]
|
||||
[XmlAttribute("transcodeSeekInfo")]
|
||||
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether timestamps should be copied.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[XmlAttribute("copyTimestamps")]
|
||||
public bool CopyTimestamps { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the encoding context.
|
||||
/// </summary>
|
||||
[DefaultValue(EncodingContext.Streaming)]
|
||||
[XmlAttribute("context")]
|
||||
public EncodingContext Context { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether subtitles are allowed in the manifest.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[XmlAttribute("enableSubtitlesInManifest")]
|
||||
public bool EnableSubtitlesInManifest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum audio channels.
|
||||
/// </summary>
|
||||
[XmlAttribute("maxAudioChannels")]
|
||||
public string? MaxAudioChannels { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum amount of segments.
|
||||
/// </summary>
|
||||
[DefaultValue(0)]
|
||||
[XmlAttribute("minSegments")]
|
||||
public int MinSegments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the segment length.
|
||||
/// </summary>
|
||||
[DefaultValue(0)]
|
||||
[XmlAttribute("segmentLength")]
|
||||
public int SegmentLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether breaking the video stream on non-keyframes is supported.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[XmlAttribute("breakOnNonKeyFrames")]
|
||||
[Obsolete("This is always false")]
|
||||
public bool? BreakOnNonKeyFrames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the profile conditions.
|
||||
/// </summary>
|
||||
public ProfileCondition[] Conditions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether variable bitrate encoding is supported.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[XmlAttribute("enableAudioVbrEncoding")]
|
||||
public bool EnableAudioVbrEncoding { get; set; } = true;
|
||||
}
|
||||
Reference in New Issue
Block a user