#pragma warning disable CA1819 // Properties should not return arrays using System; using System.ComponentModel; using System.Linq; using System.Xml.Serialization; using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Model.Dlna { /// /// A represents a set of metadata which determines which content a certain device is able to play. ///
/// Specifically, it defines the supported containers and /// codecs (video and/or audio, including codec profiles and levels) /// the device is able to direct play (without transcoding or remuxing), /// as well as which containers/codecs to transcode to in case it isn't. ///
[XmlRoot("Profile")] public class DeviceProfile { /// /// Gets or sets the name of this device profile. /// public string? Name { get; set; } /// /// Gets or sets the Id. /// [XmlIgnore] public string? Id { get; set; } /// /// Gets or sets the Identification. /// public DeviceIdentification? Identification { get; set; } /// /// Gets or sets the friendly name of the device profile, which can be shown to users. /// public string? FriendlyName { get; set; } /// /// Gets or sets the manufacturer of the device which this profile represents. /// public string? Manufacturer { get; set; } /// /// Gets or sets an url for the manufacturer of the device which this profile represents. /// public string? ManufacturerUrl { get; set; } /// /// Gets or sets the model name of the device which this profile represents. /// public string? ModelName { get; set; } /// /// Gets or sets the model description of the device which this profile represents. /// public string? ModelDescription { get; set; } /// /// Gets or sets the model number of the device which this profile represents. /// public string? ModelNumber { get; set; } /// /// Gets or sets the ModelUrl. /// public string? ModelUrl { get; set; } /// /// Gets or sets the serial number of the device which this profile represents. /// public string? SerialNumber { get; set; } /// /// Gets or sets a value indicating whether EnableAlbumArtInDidl. /// [DefaultValue(false)] public bool EnableAlbumArtInDidl { get; set; } /// /// Gets or sets a value indicating whether EnableSingleAlbumArtLimit. /// [DefaultValue(false)] public bool EnableSingleAlbumArtLimit { get; set; } /// /// Gets or sets a value indicating whether EnableSingleSubtitleLimit. /// [DefaultValue(false)] public bool EnableSingleSubtitleLimit { get; set; } /// /// Gets or sets the SupportedMediaTypes. /// public string SupportedMediaTypes { get; set; } = "Audio,Photo,Video"; /// /// Gets or sets the UserId. /// public string? UserId { get; set; } /// /// Gets or sets the AlbumArtPn. /// public string? AlbumArtPn { get; set; } /// /// Gets or sets the MaxAlbumArtWidth. /// public int? MaxAlbumArtWidth { get; set; } /// /// Gets or sets the MaxAlbumArtHeight. /// public int? MaxAlbumArtHeight { get; set; } /// /// Gets or sets the maximum allowed width of embedded icons. /// public int? MaxIconWidth { get; set; } /// /// Gets or sets the maximum allowed height of embedded icons. /// public int? MaxIconHeight { get; set; } /// /// Gets or sets the maximum allowed bitrate for all streamed content. /// public int? MaxStreamingBitrate { get; set; } = 8000000; /// /// Gets or sets the maximum allowed bitrate for statically streamed content (= direct played files). /// public int? MaxStaticBitrate { get; set; } = 8000000; /// /// Gets or sets the maximum allowed bitrate for transcoded music streams. /// public int? MusicStreamingTranscodingBitrate { get; set; } = 128000; /// /// Gets or sets the maximum allowed bitrate for statically streamed (= direct played) music files. /// public int? MaxStaticMusicBitrate { get; set; } = 8000000; /// /// Gets or sets the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace. /// public string? SonyAggregationFlags { get; set; } /// /// Gets or sets the ProtocolInfo. /// public string? ProtocolInfo { get; set; } /// /// Gets or sets the TimelineOffsetSeconds. /// [DefaultValue(0)] public int TimelineOffsetSeconds { get; set; } /// /// Gets or sets a value indicating whether RequiresPlainVideoItems. /// [DefaultValue(false)] public bool RequiresPlainVideoItems { get; set; } /// /// Gets or sets a value indicating whether RequiresPlainFolders. /// [DefaultValue(false)] public bool RequiresPlainFolders { get; set; } /// /// Gets or sets a value indicating whether EnableMSMediaReceiverRegistrar. /// [DefaultValue(false)] public bool EnableMSMediaReceiverRegistrar { get; set; } /// /// Gets or sets a value indicating whether IgnoreTranscodeByteRangeRequests. /// [DefaultValue(false)] public bool IgnoreTranscodeByteRangeRequests { get; set; } /// /// Gets or sets the XmlRootAttributes. /// public XmlAttribute[] XmlRootAttributes { get; set; } = Array.Empty(); /// /// Gets or sets the direct play profiles. /// public DirectPlayProfile[] DirectPlayProfiles { get; set; } = Array.Empty(); /// /// Gets or sets the transcoding profiles. /// public TranscodingProfile[] TranscodingProfiles { get; set; } = Array.Empty(); /// /// Gets or sets the container profiles. /// public ContainerProfile[] ContainerProfiles { get; set; } = Array.Empty(); /// /// Gets or sets the codec profiles. /// public CodecProfile[] CodecProfiles { get; set; } = Array.Empty(); /// /// Gets or sets the ResponseProfiles. /// public ResponseProfile[] ResponseProfiles { get; set; } = Array.Empty(); /// /// Gets or sets the subtitle profiles. /// public SubtitleProfile[] SubtitleProfiles { get; set; } = Array.Empty(); /// /// The GetSupportedMediaTypes. /// /// The . public MediaType[] GetSupportedMediaTypes() { return ContainerProfile.SplitValue(SupportedMediaTypes) .Select(m => Enum.TryParse(m, out var parsed) ? parsed : MediaType.Unknown) .Where(m => m != MediaType.Unknown) .ToArray(); } /// /// Gets the audio transcoding profile. /// /// The container. /// The audio Codec. /// A . public TranscodingProfile? GetAudioTranscodingProfile(string? container, string? audioCodec) { container = (container ?? string.Empty).TrimStart('.'); foreach (var i in TranscodingProfiles) { if (i.Type != DlnaProfileType.Audio) { continue; } if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) { continue; } if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { continue; } return i; } return null; } /// /// Gets the video transcoding profile. /// /// The container. /// The audio Codec. /// The video Codec. /// The . public TranscodingProfile? GetVideoTranscodingProfile(string? container, string? audioCodec, string? videoCodec) { container = (container ?? string.Empty).TrimStart('.'); foreach (var i in TranscodingProfiles) { if (i.Type != DlnaProfileType.Video) { continue; } if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) { continue; } if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { continue; } if (!string.Equals(videoCodec, i.VideoCodec, StringComparison.OrdinalIgnoreCase)) { continue; } return i; } return null; } /// /// Gets the audio media profile. /// /// The container. /// The audio codec. /// The audio channels. /// The audio bitrate. /// The audio sample rate. /// The audio bit depth. /// The . public ResponseProfile? GetAudioMediaProfile(string? container, string? audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth) { foreach (var i in ResponseProfiles) { if (i.Type != DlnaProfileType.Audio) { continue; } if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) { continue; } var audioCodecs = i.GetAudioCodecs(); if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { continue; } var anyOff = false; foreach (ProfileCondition c in i.Conditions) { if (!ConditionProcessor.IsAudioConditionSatisfied(GetModelProfileCondition(c), audioChannels, audioBitrate, audioSampleRate, audioBitDepth)) { anyOff = true; break; } } if (anyOff) { continue; } return i; } return null; } /// /// Gets the model profile condition. /// /// The c. /// The . private ProfileCondition GetModelProfileCondition(ProfileCondition c) { return new ProfileCondition { Condition = c.Condition, IsRequired = c.IsRequired, Property = c.Property, Value = c.Value }; } /// /// Gets the image media profile. /// /// The container. /// The width. /// The height. /// The . public ResponseProfile? GetImageMediaProfile(string container, int? width, int? height) { foreach (var i in ResponseProfiles) { if (i.Type != DlnaProfileType.Photo) { continue; } if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) { continue; } var anyOff = false; foreach (var c in i.Conditions) { if (!ConditionProcessor.IsImageConditionSatisfied(GetModelProfileCondition(c), width, height)) { anyOff = true; break; } } if (anyOff) { continue; } return i; } return null; } /// /// Gets the video media profile. /// /// The container. /// The audio codec. /// The video codec. /// The width. /// The height. /// The bit depth. /// The video bitrate. /// The video profile. /// The video range type. /// The video level. /// The video framerate. /// The packet length. /// The timestamp. /// True if anamorphic. /// True if interlaced. /// The ref frames. /// The number of video streams. /// The number of audio streams. /// The video Codec tag. /// True if Avc. /// The . public ResponseProfile? GetVideoMediaProfile( string? container, string? audioCodec, string? videoCodec, int? width, int? height, int? bitDepth, int? videoBitrate, string? videoProfile, VideoRangeType videoRangeType, double? videoLevel, float? videoFramerate, int? packetLength, TransportStreamTimestamp timestamp, bool? isAnamorphic, bool? isInterlaced, int? refFrames, int? numVideoStreams, int? numAudioStreams, string? videoCodecTag, bool? isAvc) { foreach (var i in ResponseProfiles) { if (i.Type != DlnaProfileType.Video) { continue; } if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) { continue; } var audioCodecs = i.GetAudioCodecs(); if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { continue; } var videoCodecs = i.GetVideoCodecs(); if (videoCodecs.Length > 0 && !videoCodecs.Contains(videoCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { continue; } var anyOff = false; foreach (ProfileCondition c in i.Conditions) { if (!ConditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) { anyOff = true; break; } } if (anyOff) { continue; } return i; } return null; } } }