aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Controller/Dlna/CodecProfile.cs11
-rw-r--r--MediaBrowser.Controller/Dlna/DeviceProfile.cs3
-rw-r--r--MediaBrowser.Controller/Dlna/TranscodingProfile.cs13
-rw-r--r--MediaBrowser.Dlna/DlnaManager.cs152
-rw-r--r--MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs116
5 files changed, 262 insertions, 33 deletions
diff --git a/MediaBrowser.Controller/Dlna/CodecProfile.cs b/MediaBrowser.Controller/Dlna/CodecProfile.cs
index bff374298..a4592e654 100644
--- a/MediaBrowser.Controller/Dlna/CodecProfile.cs
+++ b/MediaBrowser.Controller/Dlna/CodecProfile.cs
@@ -32,6 +32,12 @@ namespace MediaBrowser.Controller.Dlna
public ProfileConditionType Condition { get; set; }
public ProfileConditionValue Property { get; set; }
public string Value { get; set; }
+ public bool IsRequired { get; set; }
+
+ public ProfileCondition()
+ {
+ IsRequired = true;
+ }
}
public enum ProfileConditionType
@@ -46,11 +52,14 @@ namespace MediaBrowser.Controller.Dlna
{
AudioChannels,
AudioBitrate,
+ AudioProfile,
Filesize,
Width,
Height,
+ Has64BitOffsets,
VideoBitrate,
VideoFramerate,
- VideoLevel
+ VideoLevel,
+ VideoProfile
}
}
diff --git a/MediaBrowser.Controller/Dlna/DeviceProfile.cs b/MediaBrowser.Controller/Dlna/DeviceProfile.cs
index 91be73bba..49568e7d4 100644
--- a/MediaBrowser.Controller/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Controller/Dlna/DeviceProfile.cs
@@ -60,6 +60,9 @@ namespace MediaBrowser.Controller.Dlna
public int TimelineOffsetSeconds { get; set; }
+ public bool RequiresPlainVideoItems { get; set; }
+ public bool RequiresPlainFolders { get; set; }
+
public DeviceProfile()
{
DirectPlayProfiles = new DirectPlayProfile[] { };
diff --git a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs
index 530a44b8c..1073f74aa 100644
--- a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs
+++ b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs
@@ -11,6 +11,10 @@ namespace MediaBrowser.Controller.Dlna
public string VideoCodec { get; set; }
public string AudioCodec { get; set; }
+ public bool EstimateContentLength { get; set; }
+
+ public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
+
public List<TranscodingSetting> Settings { get; set; }
public TranscodingProfile()
@@ -27,6 +31,13 @@ namespace MediaBrowser.Controller.Dlna
public enum TranscodingSettingType
{
- Profile
+ Profile = 0,
+ MaxAudioChannels = 1
+ }
+
+ public enum TranscodeSeekInfo
+ {
+ Auto = 0,
+ Bytes = 1
}
}
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index be7295e12..24e9d83a0 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -233,9 +233,26 @@ namespace MediaBrowser.Dlna
Name = "Xbox 360",
ClientType = "DLNA",
+ ModelName = "Windows Media Player Sharing",
+ ModelNumber = "12.0",
+ ModelUrl = "http://www.microsoft.com/",
+ Manufacturer = "Microsoft Corporation",
+ ManufacturerUrl = "http://www.microsoft.com/",
+ XDlnaDoc = "DMS-1.50",
+
+ TimelineOffsetSeconds = 40,
+ RequiresPlainFolders = true,
+ RequiresPlainVideoItems = true,
+
Identification = new DeviceIdentification
{
- ModelName = "Xbox 360"
+ ModelName = "Xbox 360",
+
+ Headers = new List<HttpHeaderInfo>
+ {
+ new HttpHeaderInfo{ Name="User-Agent", Value="Xbox", Match= HeaderMatchType.Substring},
+ new HttpHeaderInfo{ Name="User-Agent", Value="Xenon", Match= HeaderMatchType.Substring}
+ }
},
TranscodingProfiles = new[]
@@ -243,12 +260,31 @@ namespace MediaBrowser.Dlna
new TranscodingProfile
{
Container = "mp3",
+ AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
- Container = "ts",
- Type = DlnaProfileType.Video
+ Container = "asf",
+ VideoCodec = "wmv2",
+ AudioCodec = "wmav2",
+ Type = DlnaProfileType.Video,
+ TranscodeSeekInfo = TranscodeSeekInfo.Bytes,
+ EstimateContentLength = true,
+
+ Settings = new List<TranscodingSetting>
+ {
+ new TranscodingSetting
+ {
+ Name = TranscodingSettingType.MaxAudioChannels,
+ Value = "6"
+ }
+ }
+ },
+ new TranscodingProfile
+ {
+ Container = "jpeg",
+ Type = DlnaProfileType.Photo
}
},
@@ -256,18 +292,59 @@ namespace MediaBrowser.Dlna
{
new DirectPlayProfile
{
- Containers = new[]{"mp3"},
- Type = DlnaProfileType.Audio
+ Containers = new[]{"avi"},
+ VideoCodec = "mpeg4",
+ AudioCodec = "ac3,mp3",
+ Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Containers = new[]{"avi"},
+ VideoCodec = "h264",
+ AudioCodec = "aac",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
- Containers = new[]{"mp4"},
+ Containers = new[]{"mp4", "mov"},
+ VideoCodec = "h264,mpeg4",
+ AudioCodec = "aac,ac3",
+ Type = DlnaProfileType.Video,
+
+ Conditions = new List<ProfileCondition>
+ {
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Has64BitOffsets, Value = "false", IsRequired=false}
+ }
+ },
+ new DirectPlayProfile
+ {
+ Containers = new[]{"asf"},
+ VideoCodec = "wmv2,wmv3,vc1",
+ AudioCodec = "wmav2,wmapro",
Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Containers = new[]{"asf"},
+ AudioCodec = "wmav2,wmapro,wmavoice",
+ Type = DlnaProfileType.Audio
+ },
+ new DirectPlayProfile
+ {
+ Containers = new[]{"mp3"},
+ AudioCodec = "mp3",
+ Type = DlnaProfileType.Audio
+ },
+ new DirectPlayProfile
+ {
+ Containers = new[]{"jpeg"},
+ Type = DlnaProfileType.Photo,
+
+ Conditions = new List<ProfileCondition>
+ {
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Width, Value = "1920"},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Height, Value = "1080"}
+ }
}
},
@@ -279,6 +356,69 @@ namespace MediaBrowser.Dlna
MimeType = "video/avi",
Type = DlnaProfileType.Video
}
+ },
+
+ CodecProfiles = new[]
+ {
+ new CodecProfile
+ {
+ Type = CodecType.VideoCodec,
+ Codec = "mpeg4",
+ Conditions = new List<ProfileCondition>
+ {
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Width, Value = "1280"},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Height, Value = "720"},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.VideoFramerate, Value = "30", IsRequired=false},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.VideoBitrate, Value = "5120000", IsRequired=false}
+ }
+ },
+
+ new CodecProfile
+ {
+ Type = CodecType.VideoCodec,
+ Codec = "h264",
+ Conditions = new List<ProfileCondition>
+ {
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Width, Value = "1920"},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Height, Value = "1080"},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.VideoLevel, Value = "41", IsRequired=false},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.VideoBitrate, Value = "10240000", IsRequired=false}
+ }
+ },
+
+ new CodecProfile
+ {
+ Type = CodecType.VideoCodec,
+ Codec = "wmv2,wmv3,vc1",
+ Conditions = new List<ProfileCondition>
+ {
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Width, Value = "1920"},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Height, Value = "1080"},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.VideoFramerate, Value = "30", IsRequired=false},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.VideoBitrate, Value = "15360000", IsRequired=false}
+ }
+ },
+
+ new CodecProfile
+ {
+ Type = CodecType.VideoAudioCodec,
+ Codec = "ac3,wmav2,wmapro",
+ Conditions = new List<ProfileCondition>
+ {
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.AudioChannels, Value = "6", IsRequired=false}
+ }
+ },
+
+ new CodecProfile
+ {
+ Type = CodecType.VideoAudioCodec,
+ Codec = "aac",
+ Conditions = new List<ProfileCondition>
+ {
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.AudioChannels, Value = "6", IsRequired=false},
+ new ProfileCondition{ Condition = ProfileConditionType.Equals, Property = ProfileConditionValue.AudioProfile, Value = "lc", IsRequired=false}
+ }
+ }
}
});
diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
index 6817c4eaa..5da845d06 100644
--- a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
@@ -36,15 +36,19 @@ namespace MediaBrowser.Dlna.PlayTo
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
- var directPlay = profile.DirectPlayProfiles
- .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream));
-
- if (directPlay != null)
+ if (profile.CodecProfiles.Where(i => i.Type == CodecType.AudioCodec)
+ .All(i => IsCodecProfileSupported(i, item.Path, null, audioStream)))
{
- playlistItem.Transcode = false;
- playlistItem.Container = Path.GetExtension(item.Path);
+ var directPlay = profile.DirectPlayProfiles
+ .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream));
- return playlistItem;
+ if (directPlay != null)
+ {
+ playlistItem.Transcode = false;
+ playlistItem.Container = Path.GetExtension(item.Path);
+
+ return playlistItem;
+ }
}
var transcodingProfile = profile.TranscodingProfiles
@@ -113,15 +117,19 @@ namespace MediaBrowser.Dlna.PlayTo
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
- var directPlay = profile.DirectPlayProfiles
- .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream));
-
- if (directPlay != null)
+ if (profile.CodecProfiles.Where(i => i.Type == CodecType.VideoCodec || i.Type == CodecType.VideoAudioCodec)
+ .All(i => IsCodecProfileSupported(i, item.Path, videoStream, audioStream)))
{
- playlistItem.Transcode = false;
- playlistItem.Container = Path.GetExtension(item.Path);
+ var directPlay = profile.DirectPlayProfiles
+ .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream));
- return playlistItem;
+ if (directPlay != null)
+ {
+ playlistItem.Transcode = false;
+ playlistItem.Container = Path.GetExtension(item.Path);
+
+ return playlistItem;
+ }
}
var transcodingProfile = profile.TranscodingProfiles
@@ -281,6 +289,24 @@ namespace MediaBrowser.Dlna.PlayTo
return true;
}
+ private bool IsCodecProfileSupported(CodecProfile profile, string mediaPath, MediaStream videoStream, MediaStream audioStream)
+ {
+ var codecs = profile.GetCodecs();
+ var stream = profile.Type == CodecType.VideoCodec ? videoStream : audioStream;
+ var existingCodec = (stream == null ? null : stream.Codec) ?? string.Empty;
+
+ if (codecs.Count == 0 || codecs.Contains(existingCodec, StringComparer.OrdinalIgnoreCase))
+ {
+ // Check additional conditions
+ if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/// <summary>
/// Determines whether [is condition satisfied] [the specified condition].
/// </summary>
@@ -292,30 +318,70 @@ namespace MediaBrowser.Dlna.PlayTo
/// <exception cref="System.InvalidOperationException">Unexpected ProfileConditionType</exception>
private bool IsConditionSatisfied(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
{
- var actualValue = GetConditionValue(condition, mediaPath, videoStream, audioStream);
+ if (condition.Property == ProfileConditionValue.VideoProfile)
+ {
+ var profile = videoStream == null ? null : videoStream.Profile;
- if (actualValue.HasValue)
+ if (!string.IsNullOrWhiteSpace(profile))
+ {
+ switch (condition.Condition)
+ {
+ case ProfileConditionType.Equals:
+ return string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
+ case ProfileConditionType.NotEquals:
+ return !string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
+ default:
+ throw new InvalidOperationException("Unexpected ProfileConditionType");
+ }
+ }
+ }
+
+ else if (condition.Property == ProfileConditionValue.AudioProfile)
{
- long expected;
- if (long.TryParse(condition.Value, NumberStyles.Any, _usCulture, out expected))
+ var profile = audioStream == null ? null : audioStream.Profile;
+
+ if (!string.IsNullOrWhiteSpace(profile))
{
switch (condition.Condition)
{
case ProfileConditionType.Equals:
- return actualValue.Value == expected;
- case ProfileConditionType.GreaterThanEqual:
- return actualValue.Value >= expected;
- case ProfileConditionType.LessThanEqual:
- return actualValue.Value <= expected;
+ return string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
case ProfileConditionType.NotEquals:
- return actualValue.Value != expected;
+ return !string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
default:
throw new InvalidOperationException("Unexpected ProfileConditionType");
}
}
}
- return false;
+ else
+ {
+ var actualValue = GetConditionValue(condition, mediaPath, videoStream, audioStream);
+
+ if (actualValue.HasValue)
+ {
+ long expected;
+ if (long.TryParse(condition.Value, NumberStyles.Any, _usCulture, out expected))
+ {
+ switch (condition.Condition)
+ {
+ case ProfileConditionType.Equals:
+ return actualValue.Value == expected;
+ case ProfileConditionType.GreaterThanEqual:
+ return actualValue.Value >= expected;
+ case ProfileConditionType.LessThanEqual:
+ return actualValue.Value <= expected;
+ case ProfileConditionType.NotEquals:
+ return actualValue.Value != expected;
+ default:
+ throw new InvalidOperationException("Unexpected ProfileConditionType");
+ }
+ }
+ }
+ }
+
+ // Value doesn't exist in metadata. Fail it if required.
+ return !condition.IsRequired;
}
/// <summary>