Add GPL modules
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
using MediaBrowser.Model.Dto;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
/// <summary>
|
||||
/// Class AudioOptions.
|
||||
/// </summary>
|
||||
public class AudioOptions
|
||||
{
|
||||
public AudioOptions()
|
||||
{
|
||||
Context = EncodingContext.Streaming;
|
||||
|
||||
EnableDirectPlay = true;
|
||||
EnableDirectStream = true;
|
||||
}
|
||||
|
||||
public bool EnableDirectPlay { get; set; }
|
||||
public bool EnableDirectStream { get; set; }
|
||||
public bool ForceDirectPlay { get; set; }
|
||||
public bool ForceDirectStream { get; set; }
|
||||
|
||||
public Guid ItemId { get; set; }
|
||||
public MediaSourceInfo[] MediaSources { get; set; }
|
||||
public DeviceProfile Profile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional. Only needed if a specific AudioStreamIndex or SubtitleStreamIndex are requested.
|
||||
/// </summary>
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows 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>
|
||||
/// The application's configured quality setting
|
||||
/// </summary>
|
||||
public long? 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 the maximum bitrate.
|
||||
/// </summary>
|
||||
/// <returns>System.Nullable<System.Int32>.</returns>
|
||||
public long? GetMaxBitrate(bool isAudio)
|
||||
{
|
||||
if (MaxBitrate.HasValue)
|
||||
{
|
||||
return MaxBitrate;
|
||||
}
|
||||
|
||||
if (Profile != null)
|
||||
{
|
||||
if (Context == EncodingContext.Static)
|
||||
{
|
||||
if (isAudio && Profile.MaxStaticMusicBitrate.HasValue)
|
||||
{
|
||||
return Profile.MaxStaticMusicBitrate;
|
||||
}
|
||||
return Profile.MaxStaticBitrate;
|
||||
}
|
||||
|
||||
return Profile.MaxStreamingBitrate;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class CodecProfile
|
||||
{
|
||||
[XmlAttribute("type")]
|
||||
public CodecType Type { get; set; }
|
||||
|
||||
public ProfileCondition[] Conditions { get; set; }
|
||||
|
||||
public ProfileCondition[] ApplyConditions { get; set; }
|
||||
|
||||
[XmlAttribute("codec")]
|
||||
public string Codec { get; set; }
|
||||
|
||||
[XmlAttribute("container")]
|
||||
public string Container { get; set; }
|
||||
|
||||
public CodecProfile()
|
||||
{
|
||||
Conditions = new ProfileCondition[] {};
|
||||
ApplyConditions = new ProfileCondition[] { };
|
||||
}
|
||||
|
||||
public string[] GetCodecs()
|
||||
{
|
||||
return ContainerProfile.SplitValue(Codec);
|
||||
}
|
||||
|
||||
private bool ContainsContainer(string container)
|
||||
{
|
||||
return ContainerProfile.ContainsContainer(Container, container);
|
||||
}
|
||||
|
||||
public bool ContainsAnyCodec(string codec, string container)
|
||||
{
|
||||
return ContainsAnyCodec(ContainerProfile.SplitValue(codec), container);
|
||||
}
|
||||
|
||||
public bool ContainsAnyCodec(string[] codec, string container)
|
||||
{
|
||||
if (!ContainsContainer(container))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var codecs = GetCodecs();
|
||||
if (codecs.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var val in codec)
|
||||
{
|
||||
if (ListHelper.ContainsIgnoreCase(codecs, val))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum CodecType
|
||||
{
|
||||
Video = 0,
|
||||
VideoAudio = 1,
|
||||
Audio = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ConditionProcessor
|
||||
{
|
||||
public bool IsVideoConditionSatisfied(ProfileCondition condition,
|
||||
int? width,
|
||||
int? height,
|
||||
int? videoBitDepth,
|
||||
int? videoBitrate,
|
||||
string videoProfile,
|
||||
double? videoLevel,
|
||||
float? videoFramerate,
|
||||
int? packetLength,
|
||||
TransportStreamTimestamp? timestamp,
|
||||
bool? isAnamorphic,
|
||||
bool? isInterlaced,
|
||||
int? refFrames,
|
||||
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.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.NumAudioStreams:
|
||||
return IsConditionSatisfied(condition, numAudioStreams);
|
||||
case ProfileConditionValue.NumVideoStreams:
|
||||
return IsConditionSatisfied(condition, numVideoStreams);
|
||||
case ProfileConditionValue.VideoTimestamp:
|
||||
return IsConditionSatisfied(condition, timestamp);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
|
||||
public 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 bool IsConditionSatisfied(ProfileCondition condition, int? currentValue)
|
||||
{
|
||||
if (!currentValue.HasValue)
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
int expected;
|
||||
if (int.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected))
|
||||
{
|
||||
switch (condition.Condition)
|
||||
{
|
||||
case ProfileConditionType.Equals:
|
||||
case ProfileConditionType.EqualsAny:
|
||||
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 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 ListHelper.ContainsIgnoreCase(expected.Split('|'), currentValue);
|
||||
}
|
||||
case ProfileConditionType.Equals:
|
||||
return StringHelper.EqualsIgnoreCase(currentValue, expected);
|
||||
case ProfileConditionType.NotEquals:
|
||||
return !StringHelper.EqualsIgnoreCase(currentValue, expected);
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsConditionSatisfied(ProfileCondition condition, bool? currentValue)
|
||||
{
|
||||
if (!currentValue.HasValue)
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
bool expected;
|
||||
if (bool.TryParse(condition.Value, out 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 bool IsConditionSatisfied(ProfileCondition condition, float currentValue)
|
||||
{
|
||||
if (currentValue <= 0)
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
float expected;
|
||||
if (float.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected))
|
||||
{
|
||||
switch (condition.Condition)
|
||||
{
|
||||
case ProfileConditionType.Equals:
|
||||
return currentValue.Equals(expected);
|
||||
case ProfileConditionType.GreaterThanEqual:
|
||||
return currentValue >= expected;
|
||||
case ProfileConditionType.LessThanEqual:
|
||||
return currentValue <= expected;
|
||||
case ProfileConditionType.NotEquals:
|
||||
return !currentValue.Equals(expected);
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsConditionSatisfied(ProfileCondition condition, double? currentValue)
|
||||
{
|
||||
if (!currentValue.HasValue)
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
double expected;
|
||||
if (double.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected))
|
||||
{
|
||||
switch (condition.Condition)
|
||||
{
|
||||
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 bool IsConditionSatisfied(ProfileCondition condition, TransportStreamTimestamp? timestamp)
|
||||
{
|
||||
if (!timestamp.HasValue)
|
||||
{
|
||||
// If the value is unknown, it satisfies if not marked as required
|
||||
return !condition.IsRequired;
|
||||
}
|
||||
|
||||
TransportStreamTimestamp 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ContainerProfile
|
||||
{
|
||||
[XmlAttribute("type")]
|
||||
public DlnaProfileType Type { get; set; }
|
||||
public ProfileCondition[] Conditions { get; set; }
|
||||
|
||||
[XmlAttribute("container")]
|
||||
public string Container { get; set; }
|
||||
|
||||
public ContainerProfile()
|
||||
{
|
||||
Conditions = new ProfileCondition[] { };
|
||||
}
|
||||
|
||||
public string[] GetContainers()
|
||||
{
|
||||
return SplitValue(Container);
|
||||
}
|
||||
|
||||
private static readonly string[] EmptyStringArray = Array.Empty<string>();
|
||||
|
||||
public static string[] SplitValue(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return EmptyStringArray;
|
||||
}
|
||||
|
||||
return value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public bool ContainsContainer(string container)
|
||||
{
|
||||
var containers = GetContainers();
|
||||
|
||||
return ContainsContainer(containers, container);
|
||||
}
|
||||
|
||||
public static bool ContainsContainer(string profileContainers, string inputContainer)
|
||||
{
|
||||
var isNegativeList = false;
|
||||
if (profileContainers != null && profileContainers.StartsWith("-"))
|
||||
{
|
||||
isNegativeList = true;
|
||||
profileContainers = profileContainers.Substring(1);
|
||||
}
|
||||
|
||||
return ContainsContainer(SplitValue(profileContainers), isNegativeList, inputContainer);
|
||||
}
|
||||
|
||||
public static bool ContainsContainer(string[] profileContainers, string inputContainer)
|
||||
{
|
||||
return ContainsContainer(profileContainers, false, inputContainer);
|
||||
}
|
||||
|
||||
public static bool ContainsContainer(string[] profileContainers, bool isNegativeList, string inputContainer)
|
||||
{
|
||||
if (profileContainers.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isNegativeList)
|
||||
{
|
||||
var allInputContainers = SplitValue(inputContainer);
|
||||
|
||||
foreach (var container in allInputContainers)
|
||||
{
|
||||
if (ListHelper.ContainsIgnoreCase(profileContainers, container))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var allInputContainers = SplitValue(inputContainer);
|
||||
|
||||
foreach (var container in allInputContainers)
|
||||
{
|
||||
if (ListHelper.ContainsIgnoreCase(profileContainers, container))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ContentFeatureBuilder
|
||||
{
|
||||
private readonly DeviceProfile _profile;
|
||||
|
||||
public ContentFeatureBuilder(DeviceProfile profile)
|
||||
{
|
||||
_profile = profile;
|
||||
}
|
||||
|
||||
public string BuildImageHeader(string container,
|
||||
int? width,
|
||||
int? height,
|
||||
bool isDirectStream,
|
||||
string orgPn = null)
|
||||
{
|
||||
string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetImageOrgOpValue();
|
||||
|
||||
// 0 = native, 1 = transcoded
|
||||
var orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
|
||||
|
||||
DlnaFlags flagValue = DlnaFlags.BackgroundTransferMode |
|
||||
DlnaFlags.InteractiveTransferMode |
|
||||
DlnaFlags.DlnaV15;
|
||||
|
||||
string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
|
||||
DlnaMaps.FlagsToString(flagValue));
|
||||
|
||||
ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container,
|
||||
width,
|
||||
height);
|
||||
|
||||
if (string.IsNullOrEmpty(orgPn))
|
||||
{
|
||||
orgPn = mediaProfile == null ? null : mediaProfile.OrgPn;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(orgPn))
|
||||
{
|
||||
orgPn = GetImageOrgPnValue(container, width, height);
|
||||
}
|
||||
|
||||
string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
|
||||
|
||||
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
|
||||
}
|
||||
|
||||
public string BuildAudioHeader(string container,
|
||||
string audioCodec,
|
||||
int? audioBitrate,
|
||||
int? audioSampleRate,
|
||||
int? audioChannels,
|
||||
int? audioBitDepth,
|
||||
bool isDirectStream,
|
||||
long? runtimeTicks,
|
||||
TranscodeSeekInfo transcodeSeekInfo)
|
||||
{
|
||||
// first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
|
||||
string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks > 0, isDirectStream, transcodeSeekInfo);
|
||||
|
||||
// 0 = native, 1 = transcoded
|
||||
string orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
|
||||
|
||||
DlnaFlags flagValue = DlnaFlags.StreamingTransferMode |
|
||||
DlnaFlags.BackgroundTransferMode |
|
||||
DlnaFlags.InteractiveTransferMode |
|
||||
DlnaFlags.DlnaV15;
|
||||
|
||||
//if (isDirectStream)
|
||||
//{
|
||||
// flagValue = flagValue | DlnaFlags.ByteBasedSeek;
|
||||
//}
|
||||
//else if (runtimeTicks.HasValue)
|
||||
//{
|
||||
// flagValue = flagValue | DlnaFlags.TimeBasedSeek;
|
||||
//}
|
||||
|
||||
string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
|
||||
DlnaMaps.FlagsToString(flagValue));
|
||||
|
||||
ResponseProfile mediaProfile = _profile.GetAudioMediaProfile(container,
|
||||
audioCodec,
|
||||
audioChannels,
|
||||
audioBitrate,
|
||||
audioSampleRate,
|
||||
audioBitDepth);
|
||||
|
||||
string orgPn = mediaProfile == null ? null : mediaProfile.OrgPn;
|
||||
|
||||
if (string.IsNullOrEmpty(orgPn))
|
||||
{
|
||||
orgPn = GetAudioOrgPnValue(container, audioBitrate, audioSampleRate, audioChannels);
|
||||
}
|
||||
|
||||
string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
|
||||
|
||||
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
|
||||
}
|
||||
|
||||
public List<string> BuildVideoHeader(string container,
|
||||
string videoCodec,
|
||||
string audioCodec,
|
||||
int? width,
|
||||
int? height,
|
||||
int? bitDepth,
|
||||
int? videoBitrate,
|
||||
TransportStreamTimestamp timestamp,
|
||||
bool isDirectStream,
|
||||
long? runtimeTicks,
|
||||
string videoProfile,
|
||||
double? videoLevel,
|
||||
float? videoFramerate,
|
||||
int? packetLength,
|
||||
TranscodeSeekInfo transcodeSeekInfo,
|
||||
bool? isAnamorphic,
|
||||
bool? isInterlaced,
|
||||
int? refFrames,
|
||||
int? numVideoStreams,
|
||||
int? numAudioStreams,
|
||||
string videoCodecTag,
|
||||
bool? isAvc)
|
||||
{
|
||||
// first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
|
||||
string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks > 0, isDirectStream, transcodeSeekInfo);
|
||||
|
||||
// 0 = native, 1 = transcoded
|
||||
string orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
|
||||
|
||||
DlnaFlags flagValue = DlnaFlags.StreamingTransferMode |
|
||||
DlnaFlags.BackgroundTransferMode |
|
||||
DlnaFlags.InteractiveTransferMode |
|
||||
DlnaFlags.DlnaV15;
|
||||
|
||||
//if (isDirectStream)
|
||||
//{
|
||||
// flagValue = flagValue | DlnaFlags.ByteBasedSeek;
|
||||
//}
|
||||
//else if (runtimeTicks.HasValue)
|
||||
//{
|
||||
// flagValue = flagValue | DlnaFlags.TimeBasedSeek;
|
||||
//}
|
||||
|
||||
string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
|
||||
DlnaMaps.FlagsToString(flagValue));
|
||||
|
||||
ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(container,
|
||||
audioCodec,
|
||||
videoCodec,
|
||||
width,
|
||||
height,
|
||||
bitDepth,
|
||||
videoBitrate,
|
||||
videoProfile,
|
||||
videoLevel,
|
||||
videoFramerate,
|
||||
packetLength,
|
||||
timestamp,
|
||||
isAnamorphic,
|
||||
isInterlaced,
|
||||
refFrames,
|
||||
numVideoStreams,
|
||||
numAudioStreams,
|
||||
videoCodecTag,
|
||||
isAvc);
|
||||
|
||||
List<string> orgPnValues = new List<string>();
|
||||
|
||||
if (mediaProfile != null && !string.IsNullOrEmpty(mediaProfile.OrgPn))
|
||||
{
|
||||
orgPnValues.AddRange(mediaProfile.OrgPn.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string s in GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, timestamp))
|
||||
{
|
||||
orgPnValues.Add(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
List<string> contentFeatureList = new List<string>();
|
||||
|
||||
foreach (string orgPn in orgPnValues)
|
||||
{
|
||||
string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn;
|
||||
|
||||
var value = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
|
||||
|
||||
contentFeatureList.Add(value);
|
||||
}
|
||||
|
||||
if (orgPnValues.Count == 0)
|
||||
{
|
||||
string contentFeatures = string.Empty;
|
||||
|
||||
var value = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
|
||||
|
||||
contentFeatureList.Add(value);
|
||||
}
|
||||
|
||||
return contentFeatureList;
|
||||
}
|
||||
|
||||
private string GetImageOrgPnValue(string container, int? width, int? height)
|
||||
{
|
||||
MediaFormatProfile? format = new MediaFormatProfileResolver()
|
||||
.ResolveImageFormat(container,
|
||||
width,
|
||||
height);
|
||||
|
||||
return format.HasValue ? format.Value.ToString() : null;
|
||||
}
|
||||
|
||||
private string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels)
|
||||
{
|
||||
MediaFormatProfile? format = new MediaFormatProfileResolver()
|
||||
.ResolveAudioFormat(container,
|
||||
audioBitrate,
|
||||
audioSampleRate,
|
||||
audioChannels);
|
||||
|
||||
return format.HasValue ? format.Value.ToString() : null;
|
||||
}
|
||||
|
||||
private string[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp)
|
||||
{
|
||||
return new MediaFormatProfileResolver().ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class DeviceIdentification
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the friendly.
|
||||
/// </summary>
|
||||
/// <value>The name of the friendly.</value>
|
||||
public string FriendlyName { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the model number.
|
||||
/// </summary>
|
||||
/// <value>The model number.</value>
|
||||
public string ModelNumber { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the serial number.
|
||||
/// </summary>
|
||||
/// <value>The serial number.</value>
|
||||
public string SerialNumber { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the model.
|
||||
/// </summary>
|
||||
/// <value>The name of the model.</value>
|
||||
public string ModelName { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the model description.
|
||||
/// </summary>
|
||||
/// <value>The model description.</value>
|
||||
public string ModelDescription { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the device description.
|
||||
/// </summary>
|
||||
/// <value>The device description.</value>
|
||||
public string DeviceDescription { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the model URL.
|
||||
/// </summary>
|
||||
/// <value>The model URL.</value>
|
||||
public string ModelUrl { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the manufacturer.
|
||||
/// </summary>
|
||||
/// <value>The manufacturer.</value>
|
||||
public string Manufacturer { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the manufacturer URL.
|
||||
/// </summary>
|
||||
/// <value>The manufacturer URL.</value>
|
||||
public string ManufacturerUrl { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the headers.
|
||||
/// </summary>
|
||||
/// <value>The headers.</value>
|
||||
public HttpHeaderInfo[] Headers { get; set; }
|
||||
|
||||
public DeviceIdentification()
|
||||
{
|
||||
Headers = new HttpHeaderInfo[] {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
[XmlRoot("Profile")]
|
||||
public class DeviceProfile
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
|
||||
[XmlIgnore]
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identification.
|
||||
/// </summary>
|
||||
/// <value>The identification.</value>
|
||||
public MediaBrowser.Model.Dlna.DeviceIdentification Identification { get; set; }
|
||||
|
||||
public string FriendlyName { get; set; }
|
||||
public string Manufacturer { get; set; }
|
||||
public string ManufacturerUrl { get; set; }
|
||||
public string ModelName { get; set; }
|
||||
public string ModelDescription { get; set; }
|
||||
public string ModelNumber { get; set; }
|
||||
public string ModelUrl { get; set; }
|
||||
public string SerialNumber { get; set; }
|
||||
|
||||
public bool EnableAlbumArtInDidl { get; set; }
|
||||
public bool EnableSingleAlbumArtLimit { get; set; }
|
||||
public bool EnableSingleSubtitleLimit { get; set; }
|
||||
|
||||
public string SupportedMediaTypes { get; set; }
|
||||
|
||||
public string UserId { get; set; }
|
||||
|
||||
public string AlbumArtPn { get; set; }
|
||||
|
||||
public int MaxAlbumArtWidth { get; set; }
|
||||
public int MaxAlbumArtHeight { get; set; }
|
||||
|
||||
public int? MaxIconWidth { get; set; }
|
||||
public int? MaxIconHeight { get; set; }
|
||||
|
||||
public long? MaxStreamingBitrate { get; set; }
|
||||
public long? MaxStaticBitrate { get; set; }
|
||||
|
||||
public int? MusicStreamingTranscodingBitrate { get; set; }
|
||||
public int? MaxStaticMusicBitrate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace.
|
||||
/// </summary>
|
||||
public string SonyAggregationFlags { get; set; }
|
||||
|
||||
public string ProtocolInfo { get; set; }
|
||||
|
||||
public int TimelineOffsetSeconds { get; set; }
|
||||
public bool RequiresPlainVideoItems { get; set; }
|
||||
public bool RequiresPlainFolders { get; set; }
|
||||
|
||||
public bool EnableMSMediaReceiverRegistrar { get; set; }
|
||||
public bool IgnoreTranscodeByteRangeRequests { get; set; }
|
||||
|
||||
public XmlAttribute[] XmlRootAttributes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the direct play profiles.
|
||||
/// </summary>
|
||||
/// <value>The direct play profiles.</value>
|
||||
public DirectPlayProfile[] DirectPlayProfiles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transcoding profiles.
|
||||
/// </summary>
|
||||
/// <value>The transcoding profiles.</value>
|
||||
public TranscodingProfile[] TranscodingProfiles { get; set; }
|
||||
|
||||
public ContainerProfile[] ContainerProfiles { get; set; }
|
||||
|
||||
public CodecProfile[] CodecProfiles { get; set; }
|
||||
public ResponseProfile[] ResponseProfiles { get; set; }
|
||||
|
||||
public SubtitleProfile[] SubtitleProfiles { get; set; }
|
||||
|
||||
public DeviceProfile()
|
||||
{
|
||||
DirectPlayProfiles = new DirectPlayProfile[] { };
|
||||
TranscodingProfiles = new TranscodingProfile[] { };
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
CodecProfiles = new CodecProfile[] { };
|
||||
ContainerProfiles = new ContainerProfile[] { };
|
||||
SubtitleProfiles = Array.Empty<SubtitleProfile>();
|
||||
|
||||
XmlRootAttributes = new XmlAttribute[] { };
|
||||
|
||||
SupportedMediaTypes = "Audio,Photo,Video";
|
||||
MaxStreamingBitrate = 8000000;
|
||||
MaxStaticBitrate = 8000000;
|
||||
MusicStreamingTranscodingBitrate = 128000;
|
||||
}
|
||||
|
||||
public string[] GetSupportedMediaTypes()
|
||||
{
|
||||
return ContainerProfile.SplitValue(SupportedMediaTypes);
|
||||
}
|
||||
|
||||
public TranscodingProfile GetAudioTranscodingProfile(string container, string audioCodec)
|
||||
{
|
||||
container = (container ?? string.Empty).TrimStart('.');
|
||||
|
||||
foreach (var i in TranscodingProfiles)
|
||||
{
|
||||
if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Audio)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!StringHelper.EqualsIgnoreCase(container, i.Container))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ListHelper.ContainsIgnoreCase(i.GetAudioCodecs(), audioCodec ?? string.Empty))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public TranscodingProfile GetVideoTranscodingProfile(string container, string audioCodec, string videoCodec)
|
||||
{
|
||||
container = (container ?? string.Empty).TrimStart('.');
|
||||
|
||||
foreach (var i in TranscodingProfiles)
|
||||
{
|
||||
if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Video)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!StringHelper.EqualsIgnoreCase(container, i.Container))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ListHelper.ContainsIgnoreCase(i.GetAudioCodecs(), audioCodec ?? string.Empty))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!StringHelper.EqualsIgnoreCase(videoCodec, i.VideoCodec ?? string.Empty))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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 && !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec ?? string.Empty))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var conditionProcessor = new ConditionProcessor();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private ProfileCondition GetModelProfileCondition(ProfileCondition c)
|
||||
{
|
||||
return new ProfileCondition
|
||||
{
|
||||
Condition = c.Condition,
|
||||
IsRequired = c.IsRequired,
|
||||
Property = c.Property,
|
||||
Value = c.Value
|
||||
};
|
||||
}
|
||||
|
||||
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 conditionProcessor = new ConditionProcessor();
|
||||
|
||||
var anyOff = false;
|
||||
foreach (ProfileCondition c in i.Conditions)
|
||||
{
|
||||
if (!conditionProcessor.IsImageConditionSatisfied(GetModelProfileCondition(c), width, height))
|
||||
{
|
||||
anyOff = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyOff)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ResponseProfile GetVideoMediaProfile(string container,
|
||||
string audioCodec,
|
||||
string videoCodec,
|
||||
int? width,
|
||||
int? height,
|
||||
int? bitDepth,
|
||||
int? videoBitrate,
|
||||
string videoProfile,
|
||||
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 && !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec ?? string.Empty))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var videoCodecs = i.GetVideoCodecs();
|
||||
if (videoCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec ?? string.Empty))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var conditionProcessor = new ConditionProcessor();
|
||||
|
||||
var anyOff = false;
|
||||
foreach (ProfileCondition c in i.Conditions)
|
||||
{
|
||||
if (!conditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
|
||||
{
|
||||
anyOff = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyOff)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class DeviceProfileInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier.
|
||||
/// </summary>
|
||||
/// <value>The identifier.</value>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type.
|
||||
/// </summary>
|
||||
/// <value>The type.</value>
|
||||
public DeviceProfileType Type { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum DeviceProfileType
|
||||
{
|
||||
System = 0,
|
||||
User = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class DirectPlayProfile
|
||||
{
|
||||
[XmlAttribute("container")]
|
||||
public string Container { get; set; }
|
||||
|
||||
[XmlAttribute("audioCodec")]
|
||||
public string AudioCodec { get; set; }
|
||||
|
||||
[XmlAttribute("videoCodec")]
|
||||
public string VideoCodec { get; set; }
|
||||
|
||||
[XmlAttribute("type")]
|
||||
public DlnaProfileType Type { get; set; }
|
||||
|
||||
public bool SupportsContainer(string container)
|
||||
{
|
||||
return ContainerProfile.ContainsContainer(Container, container);
|
||||
}
|
||||
|
||||
public bool SupportsVideoCodec(string codec)
|
||||
{
|
||||
return ContainerProfile.ContainsContainer(VideoCodec, codec);
|
||||
}
|
||||
|
||||
public bool SupportsAudioCodec(string codec)
|
||||
{
|
||||
return ContainerProfile.ContainsContainer(AudioCodec, codec);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
[Flags]
|
||||
public enum DlnaFlags : ulong
|
||||
{
|
||||
/*! <i>Background</i> transfer mode.
|
||||
For use with upload and download transfers to and from the server.
|
||||
The primary difference between \ref DH_TransferMode_Interactive and
|
||||
\ref DH_TransferMode_Bulk is that the latter assumes that the user
|
||||
is not relying on the transfer for immediately rendering the content
|
||||
and there are no issues with causing a buffer overflow if the
|
||||
receiver uses TCP flow control to reduce total throughput.
|
||||
*/
|
||||
BackgroundTransferMode = 1 << 22,
|
||||
|
||||
ByteBasedSeek = 1 << 29,
|
||||
ConnectionStall = 1 << 21,
|
||||
|
||||
DlnaV15 = 1 << 20,
|
||||
|
||||
/*! <i>Interactive</i> transfer mode.
|
||||
For best effort transfer of images and non-real-time transfers.
|
||||
URIs with image content usually support \ref DH_TransferMode_Bulk too.
|
||||
The primary difference between \ref DH_TransferMode_Interactive and
|
||||
\ref DH_TransferMode_Bulk is that the former assumes that the
|
||||
transfer is intended for immediate rendering.
|
||||
*/
|
||||
InteractiveTransferMode = 1 << 23,
|
||||
|
||||
PlayContainer = 1 << 28,
|
||||
RtspPause = 1 << 25,
|
||||
S0Increase = 1 << 27,
|
||||
SenderPaced = 1L << 31,
|
||||
SnIncrease = 1 << 26,
|
||||
|
||||
/*! <i>Streaming</i> transfer mode.
|
||||
The server transmits at a throughput sufficient for real-time playback of
|
||||
audio or video. URIs with audio or video often support the
|
||||
\ref DH_TransferMode_Interactive and \ref DH_TransferMode_Bulk transfer modes.
|
||||
The most well-known exception to this general claim is for live streams.
|
||||
*/
|
||||
StreamingTransferMode = 1 << 24,
|
||||
|
||||
TimeBasedSeek = 1 << 30
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class DlnaMaps
|
||||
{
|
||||
private static readonly string DefaultStreaming =
|
||||
FlagsToString(DlnaFlags.StreamingTransferMode |
|
||||
DlnaFlags.BackgroundTransferMode |
|
||||
DlnaFlags.ConnectionStall |
|
||||
DlnaFlags.ByteBasedSeek |
|
||||
DlnaFlags.DlnaV15);
|
||||
|
||||
private static readonly string DefaultInteractive =
|
||||
FlagsToString(DlnaFlags.InteractiveTransferMode |
|
||||
DlnaFlags.BackgroundTransferMode |
|
||||
DlnaFlags.ConnectionStall |
|
||||
DlnaFlags.ByteBasedSeek |
|
||||
DlnaFlags.DlnaV15);
|
||||
|
||||
public static string FlagsToString(DlnaFlags flags)
|
||||
{
|
||||
return string.Format("{0:X8}{1:D24}", (ulong)flags, 0);
|
||||
}
|
||||
|
||||
public static string GetOrgOpValue(bool hasKnownRuntime, bool isDirectStream, TranscodeSeekInfo profileTranscodeSeekInfo)
|
||||
{
|
||||
if (hasKnownRuntime)
|
||||
{
|
||||
string orgOp = string.Empty;
|
||||
|
||||
// Time-based seeking currently only possible when transcoding
|
||||
orgOp += isDirectStream ? "0" : "1";
|
||||
|
||||
// Byte-based seeking only possible when not transcoding
|
||||
orgOp += isDirectStream || profileTranscodeSeekInfo == TranscodeSeekInfo.Bytes ? "1" : "0";
|
||||
|
||||
return orgOp;
|
||||
}
|
||||
|
||||
// No seeking is available if we don't know the content runtime
|
||||
return "00";
|
||||
}
|
||||
|
||||
public static string GetImageOrgOpValue()
|
||||
{
|
||||
string orgOp = string.Empty;
|
||||
|
||||
// Time-based seeking currently only possible when transcoding
|
||||
orgOp += "0";
|
||||
|
||||
// Byte-based seeking only possible when not transcoding
|
||||
orgOp += "0";
|
||||
|
||||
return orgOp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum DlnaProfileType
|
||||
{
|
||||
Audio = 0,
|
||||
Video = 1,
|
||||
Photo = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum EncodingContext
|
||||
{
|
||||
Streaming = 0,
|
||||
Static = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum HeaderMatchType
|
||||
{
|
||||
Equals = 0,
|
||||
Regex = 1,
|
||||
Substring = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class HttpHeaderInfo
|
||||
{
|
||||
[XmlAttribute("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[XmlAttribute("value")]
|
||||
public string Value { get; set; }
|
||||
|
||||
[XmlAttribute("match")]
|
||||
public HeaderMatchType Match { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using MediaBrowser.Model.Events;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public interface IDeviceDiscovery
|
||||
{
|
||||
event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
|
||||
event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public interface ITranscoderSupport
|
||||
{
|
||||
bool CanEncodeToAudioCodec(string codec);
|
||||
bool CanEncodeToSubtitleCodec(string codec);
|
||||
bool CanExtractSubtitles(string codec);
|
||||
}
|
||||
|
||||
public class FullTranscoderSupport : ITranscoderSupport
|
||||
{
|
||||
public bool CanEncodeToAudioCodec(string codec)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public bool CanEncodeToSubtitleCodec(string codec)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public bool CanExtractSubtitles(string codec)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum MediaFormatProfile
|
||||
{
|
||||
MP3,
|
||||
WMA_BASE,
|
||||
WMA_FULL,
|
||||
LPCM16_44_MONO,
|
||||
LPCM16_44_STEREO,
|
||||
LPCM16_48_MONO,
|
||||
LPCM16_48_STEREO,
|
||||
AAC_ISO,
|
||||
AAC_ISO_320,
|
||||
AAC_ADTS,
|
||||
AAC_ADTS_320,
|
||||
FLAC,
|
||||
OGG,
|
||||
|
||||
JPEG_SM,
|
||||
JPEG_MED,
|
||||
JPEG_LRG,
|
||||
JPEG_TN,
|
||||
PNG_LRG,
|
||||
PNG_TN,
|
||||
GIF_LRG,
|
||||
RAW,
|
||||
|
||||
MPEG1,
|
||||
MPEG_PS_PAL,
|
||||
MPEG_PS_NTSC,
|
||||
MPEG_TS_SD_EU,
|
||||
MPEG_TS_SD_EU_ISO,
|
||||
MPEG_TS_SD_EU_T,
|
||||
MPEG_TS_SD_NA,
|
||||
MPEG_TS_SD_NA_ISO,
|
||||
MPEG_TS_SD_NA_T,
|
||||
MPEG_TS_SD_KO,
|
||||
MPEG_TS_SD_KO_ISO,
|
||||
MPEG_TS_SD_KO_T,
|
||||
MPEG_TS_JP_T,
|
||||
AVI,
|
||||
MATROSKA,
|
||||
FLV,
|
||||
DVR_MS,
|
||||
WTV,
|
||||
OGV,
|
||||
AVC_MP4_MP_SD_AAC_MULT5,
|
||||
AVC_MP4_MP_SD_MPEG1_L3,
|
||||
AVC_MP4_MP_SD_AC3,
|
||||
AVC_MP4_MP_HD_720p_AAC,
|
||||
AVC_MP4_MP_HD_1080i_AAC,
|
||||
AVC_MP4_HP_HD_AAC,
|
||||
AVC_TS_MP_HD_AAC_MULT5,
|
||||
AVC_TS_MP_HD_AAC_MULT5_T,
|
||||
AVC_TS_MP_HD_AAC_MULT5_ISO,
|
||||
AVC_TS_MP_HD_MPEG1_L3,
|
||||
AVC_TS_MP_HD_MPEG1_L3_T,
|
||||
AVC_TS_MP_HD_MPEG1_L3_ISO,
|
||||
AVC_TS_MP_HD_AC3,
|
||||
AVC_TS_MP_HD_AC3_T,
|
||||
AVC_TS_MP_HD_AC3_ISO,
|
||||
AVC_TS_HP_HD_MPEG1_L2_T,
|
||||
AVC_TS_HP_HD_MPEG1_L2_ISO,
|
||||
AVC_TS_MP_SD_AAC_MULT5,
|
||||
AVC_TS_MP_SD_AAC_MULT5_T,
|
||||
AVC_TS_MP_SD_AAC_MULT5_ISO,
|
||||
AVC_TS_MP_SD_MPEG1_L3,
|
||||
AVC_TS_MP_SD_MPEG1_L3_T,
|
||||
AVC_TS_MP_SD_MPEG1_L3_ISO,
|
||||
AVC_TS_HP_SD_MPEG1_L2_T,
|
||||
AVC_TS_HP_SD_MPEG1_L2_ISO,
|
||||
AVC_TS_MP_SD_AC3,
|
||||
AVC_TS_MP_SD_AC3_T,
|
||||
AVC_TS_MP_SD_AC3_ISO,
|
||||
AVC_TS_HD_DTS_T,
|
||||
AVC_TS_HD_DTS_ISO,
|
||||
WMVMED_BASE,
|
||||
WMVMED_FULL,
|
||||
WMVMED_PRO,
|
||||
WMVHIGH_FULL,
|
||||
WMVHIGH_PRO,
|
||||
VC1_ASF_AP_L1_WMA,
|
||||
VC1_ASF_AP_L2_WMA,
|
||||
VC1_ASF_AP_L3_WMA,
|
||||
VC1_TS_AP_L1_AC3_ISO,
|
||||
VC1_TS_AP_L2_AC3_ISO,
|
||||
VC1_TS_HD_DTS_ISO,
|
||||
VC1_TS_HD_DTS_T,
|
||||
MPEG4_P2_MP4_ASP_AAC,
|
||||
MPEG4_P2_MP4_SP_L6_AAC,
|
||||
MPEG4_P2_MP4_NDSD,
|
||||
MPEG4_P2_TS_ASP_AAC,
|
||||
MPEG4_P2_TS_ASP_AAC_T,
|
||||
MPEG4_P2_TS_ASP_AAC_ISO,
|
||||
MPEG4_P2_TS_ASP_MPEG1_L3,
|
||||
MPEG4_P2_TS_ASP_MPEG1_L3_T,
|
||||
MPEG4_P2_TS_ASP_MPEG1_L3_ISO,
|
||||
MPEG4_P2_TS_ASP_MPEG2_L2,
|
||||
MPEG4_P2_TS_ASP_MPEG2_L2_T,
|
||||
MPEG4_P2_TS_ASP_MPEG2_L2_ISO,
|
||||
MPEG4_P2_TS_ASP_AC3,
|
||||
MPEG4_P2_TS_ASP_AC3_T,
|
||||
MPEG4_P2_TS_ASP_AC3_ISO,
|
||||
AVC_TS_HD_50_LPCM_T,
|
||||
AVC_MP4_LPCM,
|
||||
MPEG4_P2_3GPP_SP_L0B_AAC,
|
||||
MPEG4_P2_3GPP_SP_L0B_AMR,
|
||||
AVC_3GPP_BL_QCIF15_AAC,
|
||||
MPEG4_H263_3GPP_P0_L10_AMR,
|
||||
MPEG4_H263_MP4_P0_L10_AAC
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class MediaFormatProfileResolver
|
||||
{
|
||||
public string[] ResolveVideoFormat(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
|
||||
{
|
||||
return ResolveVideoFormatInternal(container, videoCodec, audioCodec, width, height, timestampType)
|
||||
.Select(i => i.ToString())
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private MediaFormatProfile[] ResolveVideoFormatInternal(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(container, "asf"))
|
||||
{
|
||||
MediaFormatProfile? val = ResolveVideoASFFormat(videoCodec, audioCodec, width, height);
|
||||
return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
|
||||
}
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "mp4"))
|
||||
{
|
||||
MediaFormatProfile? val = ResolveVideoMP4Format(videoCodec, audioCodec, width, height);
|
||||
return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
|
||||
}
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "avi"))
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.AVI };
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "mkv"))
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.MATROSKA };
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "mpeg2ps") ||
|
||||
StringHelper.EqualsIgnoreCase(container, "ts"))
|
||||
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.MPEG_PS_NTSC, MediaFormatProfile.MPEG_PS_PAL };
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "mpeg1video"))
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.MPEG1 };
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "mpeg2ts") ||
|
||||
StringHelper.EqualsIgnoreCase(container, "mpegts") ||
|
||||
StringHelper.EqualsIgnoreCase(container, "m2ts"))
|
||||
{
|
||||
|
||||
return ResolveVideoMPEG2TSFormat(videoCodec, audioCodec, width, height, timestampType);
|
||||
}
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "flv"))
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.FLV };
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "wtv"))
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.WTV };
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "3gp"))
|
||||
{
|
||||
MediaFormatProfile? val = ResolveVideo3GPFormat(videoCodec, audioCodec);
|
||||
return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { };
|
||||
}
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "ogv") || StringHelper.EqualsIgnoreCase(container, "ogg"))
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.OGV };
|
||||
|
||||
return new MediaFormatProfile[] { };
|
||||
}
|
||||
|
||||
private MediaFormatProfile[] ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType)
|
||||
{
|
||||
string suffix = "";
|
||||
|
||||
switch (timestampType)
|
||||
{
|
||||
case TransportStreamTimestamp.None:
|
||||
suffix = "_ISO";
|
||||
break;
|
||||
case TransportStreamTimestamp.Valid:
|
||||
suffix = "_T";
|
||||
break;
|
||||
}
|
||||
|
||||
string resolution = "S";
|
||||
if ((width.HasValue && width.Value > 720) || (height.HasValue && height.Value > 576))
|
||||
{
|
||||
resolution = "H";
|
||||
}
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg2video"))
|
||||
{
|
||||
List<MediaFormatProfile> list = new List<MediaFormatProfile>();
|
||||
|
||||
list.Add(ValueOf("MPEG_TS_SD_NA" + suffix));
|
||||
list.Add(ValueOf("MPEG_TS_SD_EU" + suffix));
|
||||
list.Add(ValueOf("MPEG_TS_SD_KO" + suffix));
|
||||
|
||||
if ((timestampType == TransportStreamTimestamp.Valid) && StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
|
||||
{
|
||||
list.Add(MediaFormatProfile.MPEG_TS_JP_T);
|
||||
}
|
||||
return list.ToArray(list.Count);
|
||||
}
|
||||
if (StringHelper.EqualsIgnoreCase(videoCodec, "h264"))
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "lpcm"))
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_50_LPCM_T };
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "dts"))
|
||||
{
|
||||
if (timestampType == TransportStreamTimestamp.None)
|
||||
{
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_DTS_ISO };
|
||||
}
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_DTS_T };
|
||||
}
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2"))
|
||||
{
|
||||
if (timestampType == TransportStreamTimestamp.None)
|
||||
{
|
||||
return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_ISO", resolution)) };
|
||||
}
|
||||
|
||||
return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) };
|
||||
}
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
|
||||
return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) };
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
|
||||
return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) };
|
||||
|
||||
if (string.IsNullOrEmpty(audioCodec) ||
|
||||
StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
|
||||
return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) };
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase(videoCodec, "vc1"))
|
||||
{
|
||||
if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
|
||||
{
|
||||
if ((width.HasValue && width.Value > 720) || (height.HasValue && height.Value > 576))
|
||||
{
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.VC1_TS_AP_L2_AC3_ISO };
|
||||
}
|
||||
return new MediaFormatProfile[] { MediaFormatProfile.VC1_TS_AP_L1_AC3_ISO };
|
||||
}
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "dts"))
|
||||
{
|
||||
suffix = StringHelper.EqualsIgnoreCase(suffix, "_ISO") ? suffix : "_T";
|
||||
|
||||
return new MediaFormatProfile[] { ValueOf(string.Format("VC1_TS_HD_DTS{0}", suffix)) };
|
||||
}
|
||||
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") || StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4"))
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
|
||||
return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AAC{0}", suffix)) };
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
|
||||
return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) };
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2"))
|
||||
return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) };
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
|
||||
return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AC3{0}", suffix)) };
|
||||
}
|
||||
|
||||
return new MediaFormatProfile[]{};
|
||||
}
|
||||
|
||||
private MediaFormatProfile ValueOf(string value)
|
||||
{
|
||||
return (MediaFormatProfile)Enum.Parse(typeof(MediaFormatProfile), value, true);
|
||||
}
|
||||
|
||||
private MediaFormatProfile? ResolveVideoMP4Format(string videoCodec, string audioCodec, int? width, int? height)
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(videoCodec, "h264"))
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "lpcm"))
|
||||
return MediaFormatProfile.AVC_MP4_LPCM;
|
||||
if (string.IsNullOrEmpty(audioCodec) ||
|
||||
StringHelper.EqualsIgnoreCase(audioCodec, "ac3"))
|
||||
{
|
||||
return MediaFormatProfile.AVC_MP4_MP_SD_AC3;
|
||||
}
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
|
||||
{
|
||||
return MediaFormatProfile.AVC_MP4_MP_SD_MPEG1_L3;
|
||||
}
|
||||
if (width.HasValue && height.HasValue)
|
||||
{
|
||||
if ((width.Value <= 720) && (height.Value <= 576))
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
|
||||
return MediaFormatProfile.AVC_MP4_MP_SD_AAC_MULT5;
|
||||
}
|
||||
else if ((width.Value <= 1280) && (height.Value <= 720))
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
|
||||
return MediaFormatProfile.AVC_MP4_MP_HD_720p_AAC;
|
||||
}
|
||||
else if ((width.Value <= 1920) && (height.Value <= 1080))
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
|
||||
{
|
||||
return MediaFormatProfile.AVC_MP4_MP_HD_1080i_AAC;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") ||
|
||||
StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4"))
|
||||
{
|
||||
if (width.HasValue && height.HasValue && width.Value <= 720 && height.Value <= 576)
|
||||
{
|
||||
if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
|
||||
return MediaFormatProfile.MPEG4_P2_MP4_ASP_AAC;
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "ac3") || StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
|
||||
{
|
||||
return MediaFormatProfile.MPEG4_P2_MP4_NDSD;
|
||||
}
|
||||
}
|
||||
else if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
|
||||
{
|
||||
return MediaFormatProfile.MPEG4_P2_MP4_SP_L6_AAC;
|
||||
}
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase(videoCodec, "h263") && StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
|
||||
{
|
||||
return MediaFormatProfile.MPEG4_H263_MP4_P0_L10_AAC;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MediaFormatProfile? ResolveVideo3GPFormat(string videoCodec, string audioCodec)
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(videoCodec, "h264"))
|
||||
{
|
||||
if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac"))
|
||||
return MediaFormatProfile.AVC_3GPP_BL_QCIF15_AAC;
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") ||
|
||||
StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4"))
|
||||
{
|
||||
if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma"))
|
||||
return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AAC;
|
||||
if (StringHelper.EqualsIgnoreCase(audioCodec, "amrnb"))
|
||||
return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AMR;
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase(videoCodec, "h263") && StringHelper.EqualsIgnoreCase(audioCodec, "amrnb"))
|
||||
{
|
||||
return MediaFormatProfile.MPEG4_H263_3GPP_P0_L10_AMR;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MediaFormatProfile? ResolveVideoASFFormat(string videoCodec, string audioCodec, int? width, int? height)
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(videoCodec, "wmv") &&
|
||||
(string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma") || StringHelper.EqualsIgnoreCase(videoCodec, "wmapro")))
|
||||
{
|
||||
|
||||
if (width.HasValue && height.HasValue)
|
||||
{
|
||||
if ((width.Value <= 720) && (height.Value <= 576))
|
||||
{
|
||||
if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma"))
|
||||
{
|
||||
return MediaFormatProfile.WMVMED_FULL;
|
||||
}
|
||||
return MediaFormatProfile.WMVMED_PRO;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma"))
|
||||
{
|
||||
return MediaFormatProfile.WMVHIGH_FULL;
|
||||
}
|
||||
return MediaFormatProfile.WMVHIGH_PRO;
|
||||
}
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(videoCodec, "vc1"))
|
||||
{
|
||||
if (width.HasValue && height.HasValue)
|
||||
{
|
||||
if ((width.Value <= 720) && (height.Value <= 576))
|
||||
return MediaFormatProfile.VC1_ASF_AP_L1_WMA;
|
||||
if ((width.Value <= 1280) && (height.Value <= 720))
|
||||
return MediaFormatProfile.VC1_ASF_AP_L2_WMA;
|
||||
if ((width.Value <= 1920) && (height.Value <= 1080))
|
||||
return MediaFormatProfile.VC1_ASF_AP_L3_WMA;
|
||||
}
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg2video"))
|
||||
{
|
||||
return MediaFormatProfile.DVR_MS;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public MediaFormatProfile? ResolveAudioFormat(string container, int? bitrate, int? frequency, int? channels)
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(container, "asf"))
|
||||
return ResolveAudioASFFormat(bitrate);
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "mp3"))
|
||||
return MediaFormatProfile.MP3;
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "lpcm"))
|
||||
return ResolveAudioLPCMFormat(frequency, channels);
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "mp4") ||
|
||||
StringHelper.EqualsIgnoreCase(container, "aac"))
|
||||
return ResolveAudioMP4Format(bitrate);
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "adts"))
|
||||
return ResolveAudioADTSFormat(bitrate);
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "flac"))
|
||||
return MediaFormatProfile.FLAC;
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "oga") ||
|
||||
StringHelper.EqualsIgnoreCase(container, "ogg"))
|
||||
return MediaFormatProfile.OGG;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MediaFormatProfile ResolveAudioASFFormat(int? bitrate)
|
||||
{
|
||||
if (bitrate.HasValue && bitrate.Value <= 193)
|
||||
{
|
||||
return MediaFormatProfile.WMA_BASE;
|
||||
}
|
||||
return MediaFormatProfile.WMA_FULL;
|
||||
}
|
||||
|
||||
private MediaFormatProfile? ResolveAudioLPCMFormat(int? frequency, int? channels)
|
||||
{
|
||||
if (frequency.HasValue && channels.HasValue)
|
||||
{
|
||||
if (frequency.Value == 44100 && channels.Value == 1)
|
||||
{
|
||||
return MediaFormatProfile.LPCM16_44_MONO;
|
||||
}
|
||||
if (frequency.Value == 44100 && channels.Value == 2)
|
||||
{
|
||||
return MediaFormatProfile.LPCM16_44_STEREO;
|
||||
}
|
||||
if (frequency.Value == 48000 && channels.Value == 1)
|
||||
{
|
||||
return MediaFormatProfile.LPCM16_48_MONO;
|
||||
}
|
||||
if (frequency.Value == 48000 && channels.Value == 2)
|
||||
{
|
||||
return MediaFormatProfile.LPCM16_48_STEREO;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return MediaFormatProfile.LPCM16_48_STEREO;
|
||||
}
|
||||
|
||||
private MediaFormatProfile ResolveAudioMP4Format(int? bitrate)
|
||||
{
|
||||
if (bitrate.HasValue && bitrate.Value <= 320)
|
||||
{
|
||||
return MediaFormatProfile.AAC_ISO_320;
|
||||
}
|
||||
return MediaFormatProfile.AAC_ISO;
|
||||
}
|
||||
|
||||
private MediaFormatProfile ResolveAudioADTSFormat(int? bitrate)
|
||||
{
|
||||
if (bitrate.HasValue && bitrate.Value <= 320)
|
||||
{
|
||||
return MediaFormatProfile.AAC_ADTS_320;
|
||||
}
|
||||
return MediaFormatProfile.AAC_ADTS;
|
||||
}
|
||||
|
||||
public MediaFormatProfile? ResolveImageFormat(string container, int? width, int? height)
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(container, "jpeg") ||
|
||||
StringHelper.EqualsIgnoreCase(container, "jpg"))
|
||||
return ResolveImageJPGFormat(width, height);
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "png"))
|
||||
return ResolveImagePNGFormat(width, height);
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "gif"))
|
||||
return MediaFormatProfile.GIF_LRG;
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase(container, "raw"))
|
||||
return MediaFormatProfile.RAW;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MediaFormatProfile ResolveImageJPGFormat(int? width, int? height)
|
||||
{
|
||||
if (width.HasValue && height.HasValue)
|
||||
{
|
||||
if ((width.Value <= 160) && (height.Value <= 160))
|
||||
return MediaFormatProfile.JPEG_TN;
|
||||
|
||||
if ((width.Value <= 640) && (height.Value <= 480))
|
||||
return MediaFormatProfile.JPEG_SM;
|
||||
|
||||
if ((width.Value <= 1024) && (height.Value <= 768))
|
||||
{
|
||||
return MediaFormatProfile.JPEG_MED;
|
||||
}
|
||||
|
||||
return MediaFormatProfile.JPEG_LRG;
|
||||
}
|
||||
|
||||
return MediaFormatProfile.JPEG_SM;
|
||||
}
|
||||
|
||||
private MediaFormatProfile ResolveImagePNGFormat(int? width, int? height)
|
||||
{
|
||||
if (width.HasValue && height.HasValue)
|
||||
{
|
||||
if ((width.Value <= 160) && (height.Value <= 160))
|
||||
return MediaFormatProfile.PNG_TN;
|
||||
}
|
||||
|
||||
return MediaFormatProfile.PNG_LRG;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum PlaybackErrorCode
|
||||
{
|
||||
NotAllowed = 0,
|
||||
NoCompatibleStream = 1,
|
||||
RateLimitExceeded = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ProfileCondition
|
||||
{
|
||||
[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; }
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum ProfileConditionType
|
||||
{
|
||||
Equals = 0,
|
||||
NotEquals = 1,
|
||||
LessThanEqual = 2,
|
||||
GreaterThanEqual = 3,
|
||||
EqualsAny = 4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ResolutionConfiguration
|
||||
{
|
||||
public int MaxWidth { get; set; }
|
||||
public int MaxBitrate { get; set; }
|
||||
|
||||
public ResolutionConfiguration(int maxWidth, int maxBitrate)
|
||||
{
|
||||
MaxWidth = maxWidth;
|
||||
MaxBitrate = maxBitrate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ResolutionNormalizer
|
||||
{
|
||||
private static readonly ResolutionConfiguration[] Configurations =
|
||||
new []
|
||||
{
|
||||
new ResolutionConfiguration(426, 320000),
|
||||
new ResolutionConfiguration(640, 400000),
|
||||
new ResolutionConfiguration(720, 950000),
|
||||
new ResolutionConfiguration(1280, 2500000),
|
||||
new ResolutionConfiguration(1920, 4000000),
|
||||
new ResolutionConfiguration(3840, 35000000)
|
||||
};
|
||||
|
||||
public static ResolutionOptions Normalize(int? inputBitrate,
|
||||
int? unused1,
|
||||
int? unused2,
|
||||
int outputBitrate,
|
||||
string inputCodec,
|
||||
string outputCodec,
|
||||
int? maxWidth,
|
||||
int? maxHeight)
|
||||
{
|
||||
// If the bitrate isn't changing, then don't downlscale the resolution
|
||||
if (inputBitrate.HasValue && outputBitrate >= inputBitrate.Value)
|
||||
{
|
||||
if (maxWidth.HasValue || maxHeight.HasValue)
|
||||
{
|
||||
return new ResolutionOptions
|
||||
{
|
||||
MaxWidth = maxWidth,
|
||||
MaxHeight = maxHeight
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var resolutionConfig = GetResolutionConfiguration(outputBitrate);
|
||||
if (resolutionConfig != null)
|
||||
{
|
||||
var originvalValue = maxWidth;
|
||||
|
||||
maxWidth = Math.Min(resolutionConfig.MaxWidth, maxWidth ?? resolutionConfig.MaxWidth);
|
||||
if (!originvalValue.HasValue || originvalValue.Value != maxWidth.Value)
|
||||
{
|
||||
maxHeight = null;
|
||||
}
|
||||
}
|
||||
|
||||
return new ResolutionOptions
|
||||
{
|
||||
MaxWidth = maxWidth,
|
||||
MaxHeight = maxHeight
|
||||
};
|
||||
}
|
||||
|
||||
private static ResolutionConfiguration GetResolutionConfiguration(int outputBitrate)
|
||||
{
|
||||
ResolutionConfiguration previousOption = null;
|
||||
|
||||
foreach (var config in Configurations)
|
||||
{
|
||||
if (outputBitrate <= config.MaxBitrate)
|
||||
{
|
||||
return previousOption ?? config;
|
||||
}
|
||||
|
||||
previousOption = config;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static double GetVideoBitrateScaleFactor(string codec)
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase(codec, "h265") ||
|
||||
StringHelper.EqualsIgnoreCase(codec, "hevc") ||
|
||||
StringHelper.EqualsIgnoreCase(codec, "vp9"))
|
||||
{
|
||||
return .5;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ResolutionOptions
|
||||
{
|
||||
public int? MaxWidth { get; set; }
|
||||
public int? MaxHeight { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class ResponseProfile
|
||||
{
|
||||
[XmlAttribute("container")]
|
||||
public string Container { get; set; }
|
||||
|
||||
[XmlAttribute("audioCodec")]
|
||||
public string AudioCodec { get; set; }
|
||||
|
||||
[XmlAttribute("videoCodec")]
|
||||
public string VideoCodec { get; set; }
|
||||
|
||||
[XmlAttribute("type")]
|
||||
public DlnaProfileType Type { get; set; }
|
||||
|
||||
[XmlAttribute("orgPn")]
|
||||
public string OrgPn { get; set; }
|
||||
|
||||
[XmlAttribute("mimeType")]
|
||||
public string MimeType { get; set; }
|
||||
|
||||
public ProfileCondition[] Conditions { get; set; }
|
||||
|
||||
public ResponseProfile()
|
||||
{
|
||||
Conditions = new ProfileCondition[] {};
|
||||
}
|
||||
|
||||
public string[] GetContainers()
|
||||
{
|
||||
return ContainerProfile.SplitValue(Container);
|
||||
}
|
||||
|
||||
public string[] GetAudioCodecs()
|
||||
{
|
||||
return ContainerProfile.SplitValue(AudioCodec);
|
||||
}
|
||||
|
||||
public string[] GetVideoCodecs()
|
||||
{
|
||||
return ContainerProfile.SplitValue(VideoCodec);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class SearchCriteria
|
||||
{
|
||||
public SearchType SearchType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Splits the specified string.
|
||||
/// </summary>
|
||||
/// <param name="str">The string.</param>
|
||||
/// <param name="term">The term.</param>
|
||||
/// <param name="limit">The limit.</param>
|
||||
/// <returns>System.String[].</returns>
|
||||
private string[] RegexSplit(string str, string term, int limit)
|
||||
{
|
||||
return new Regex(term).Split(str, limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the specified string.
|
||||
/// </summary>
|
||||
/// <param name="str">The string.</param>
|
||||
/// <param name="term">The term.</param>
|
||||
/// <returns>System.String[].</returns>
|
||||
private string[] RegexSplit(string str, string term)
|
||||
{
|
||||
return Regex.Split(str, term, RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public SearchCriteria(string search)
|
||||
{
|
||||
if (string.IsNullOrEmpty(search))
|
||||
{
|
||||
throw new ArgumentNullException("search");
|
||||
}
|
||||
|
||||
SearchType = SearchType.Unknown;
|
||||
|
||||
String[] factors = RegexSplit(search, "(and|or)");
|
||||
foreach (String factor in factors)
|
||||
{
|
||||
String[] subFactors = RegexSplit(factor.Trim().Trim('(').Trim(')').Trim(), "\\s", 3);
|
||||
|
||||
if (subFactors.Length == 3)
|
||||
{
|
||||
|
||||
if (StringHelper.EqualsIgnoreCase("upnp:class", subFactors[0]) &&
|
||||
(StringHelper.EqualsIgnoreCase("=", subFactors[1]) || StringHelper.EqualsIgnoreCase("derivedfrom", subFactors[1])))
|
||||
{
|
||||
if (StringHelper.EqualsIgnoreCase("\"object.item.imageItem\"", subFactors[2]) || StringHelper.EqualsIgnoreCase("\"object.item.imageItem.photo\"", subFactors[2]))
|
||||
{
|
||||
SearchType = SearchType.Image;
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("\"object.item.videoItem\"", subFactors[2]))
|
||||
{
|
||||
SearchType = SearchType.Video;
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("\"object.container.playlistContainer\"", subFactors[2]))
|
||||
{
|
||||
SearchType = SearchType.Playlist;
|
||||
}
|
||||
else if (StringHelper.EqualsIgnoreCase("\"object.container.album.musicAlbum\"", subFactors[2]))
|
||||
{
|
||||
SearchType = SearchType.MusicAlbum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum SearchType
|
||||
{
|
||||
Unknown = 0,
|
||||
Audio = 1,
|
||||
Image = 2,
|
||||
Video = 3,
|
||||
Playlist = 4,
|
||||
MusicAlbum = 5
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class SortCriteria
|
||||
{
|
||||
public SortOrder SortOrder
|
||||
{
|
||||
get { return SortOrder.Ascending; }
|
||||
}
|
||||
|
||||
public SortCriteria(string value)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum SubtitleDeliveryMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// The encode
|
||||
/// </summary>
|
||||
Encode = 0,
|
||||
/// <summary>
|
||||
/// The embed
|
||||
/// </summary>
|
||||
Embed = 1,
|
||||
/// <summary>
|
||||
/// The external
|
||||
/// </summary>
|
||||
External = 2,
|
||||
/// <summary>
|
||||
/// The HLS
|
||||
/// </summary>
|
||||
Hls = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class SubtitleProfile
|
||||
{
|
||||
[XmlAttribute("format")]
|
||||
public string Format { get; set; }
|
||||
|
||||
[XmlAttribute("method")]
|
||||
public SubtitleDeliveryMethod Method { get; set; }
|
||||
|
||||
[XmlAttribute("didlMode")]
|
||||
public string DidlMode { get; set; }
|
||||
|
||||
[XmlAttribute("language")]
|
||||
public string Language { get; set; }
|
||||
|
||||
[XmlAttribute("container")]
|
||||
public string Container { get; set; }
|
||||
|
||||
public string[] GetLanguages()
|
||||
{
|
||||
return ContainerProfile.SplitValue(Language);
|
||||
}
|
||||
|
||||
public bool SupportsLanguage(string subLanguage)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Language))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(subLanguage))
|
||||
{
|
||||
subLanguage = "und";
|
||||
}
|
||||
|
||||
var languages = GetLanguages();
|
||||
return languages.Length == 0 || ListHelper.ContainsIgnoreCase(languages, subLanguage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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,8 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public enum TranscodeSeekInfo
|
||||
{
|
||||
Auto = 0,
|
||||
Bytes = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class TranscodingProfile
|
||||
{
|
||||
[XmlAttribute("container")]
|
||||
public string Container { get; set; }
|
||||
|
||||
[XmlAttribute("type")]
|
||||
public DlnaProfileType Type { get; set; }
|
||||
|
||||
[XmlAttribute("videoCodec")]
|
||||
public string VideoCodec { get; set; }
|
||||
|
||||
[XmlAttribute("audioCodec")]
|
||||
public string AudioCodec { get; set; }
|
||||
|
||||
[XmlAttribute("protocol")]
|
||||
public string Protocol { get; set; }
|
||||
|
||||
[XmlAttribute("estimateContentLength")]
|
||||
public bool EstimateContentLength { get; set; }
|
||||
|
||||
[XmlAttribute("enableMpegtsM2TsMode")]
|
||||
public bool EnableMpegtsM2TsMode { get; set; }
|
||||
|
||||
[XmlAttribute("transcodeSeekInfo")]
|
||||
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
|
||||
|
||||
[XmlAttribute("copyTimestamps")]
|
||||
public bool CopyTimestamps { get; set; }
|
||||
|
||||
[XmlAttribute("context")]
|
||||
public EncodingContext Context { get; set; }
|
||||
|
||||
[XmlAttribute("enableSubtitlesInManifest")]
|
||||
public bool EnableSubtitlesInManifest { get; set; }
|
||||
|
||||
[XmlAttribute("maxAudioChannels")]
|
||||
public string MaxAudioChannels { get; set; }
|
||||
|
||||
[XmlAttribute("minSegments")]
|
||||
public int MinSegments { get; set; }
|
||||
|
||||
[XmlAttribute("segmentLength")]
|
||||
public int SegmentLength { get; set; }
|
||||
|
||||
[XmlAttribute("breakOnNonKeyFrames")]
|
||||
public bool BreakOnNonKeyFrames { get; set; }
|
||||
|
||||
public string[] GetAudioCodecs()
|
||||
{
|
||||
return ContainerProfile.SplitValue(AudioCodec);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Net;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class UpnpDeviceInfo
|
||||
{
|
||||
public Uri Location { get; set; }
|
||||
public Dictionary<string, string> Headers { get; set; }
|
||||
public IpAddressInfo LocalIpAddress { get; set; }
|
||||
public int LocalPort { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
/// <summary>
|
||||
/// Class VideoOptions.
|
||||
/// </summary>
|
||||
public class VideoOptions : AudioOptions
|
||||
{
|
||||
public int? AudioStreamIndex { get; set; }
|
||||
public int? SubtitleStreamIndex { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class XmlAttribute
|
||||
{
|
||||
[XmlAttribute("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[XmlAttribute("value")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user