123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- 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;
- }
- }
- }
|