From 5170042eb5efee7be005dcc5aca863b66c23a6f2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 18 Apr 2014 13:16:25 -0400 Subject: [PATCH] support more dlna resource properties --- .../Playback/BaseStreamingService.cs | 4 +- MediaBrowser.Dlna/DlnaManager.cs | 10 +- MediaBrowser.Dlna/Server/ControlHandler.cs | 64 ++++---- .../Server/DescriptionXmlBuilder.cs | 2 +- .../Server/DlnaServerEntryPoint.cs | 4 +- .../Configuration/ServerConfiguration.cs | 3 + .../Dlna/MediaFormatProfileResolver.cs | 126 ++++++++------- MediaBrowser.Model/Dlna/StreamBuilder.cs | 6 +- MediaBrowser.Model/Dlna/StreamInfo.cs | 146 +++++++++++++++++- .../Localization/LocalizationManager.cs | 1 + .../Localization/Server/server.json | 4 +- 11 files changed, 271 insertions(+), 99 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 04deea3cf..ff9596757 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -380,7 +380,7 @@ namespace MediaBrowser.Api.Playback if (isVc1) { - profileScore ++; + profileScore++; // Max of 2 profileScore = Math.Min(profileScore, 2); } @@ -445,7 +445,7 @@ namespace MediaBrowser.Api.Playback { if (state.AudioStream != null && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5) { - volParam = ",volume=2.000000"; + volParam = ",volume=" + ServerConfigurationManager.Configuration.DownMixAudioBoost.ToString(UsCulture); } } diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index 9993b0e21..c2a7ee35f 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -516,7 +516,15 @@ namespace MediaBrowser.Dlna var serverAddress = device.Descriptor.ToString().Substring(0, device.Descriptor.ToString().IndexOf("/dlna", StringComparison.OrdinalIgnoreCase)); - return new ControlHandler(_logger, _userManager, _libraryManager, profile, serverAddress, _dtoService, _imageProcessor, _userDataManager) + return new ControlHandler( + _logger, + _userManager, + _libraryManager, + profile, + serverAddress, + _dtoService, + _imageProcessor, + _userDataManager) .ProcessControlRequest(request); } diff --git a/MediaBrowser.Dlna/Server/ControlHandler.cs b/MediaBrowser.Dlna/Server/ControlHandler.cs index c3a3c0bf0..ba325a66b 100644 --- a/MediaBrowser.Dlna/Server/ControlHandler.cs +++ b/MediaBrowser.Dlna/Server/ControlHandler.cs @@ -89,6 +89,8 @@ namespace MediaBrowser.Dlna.Server sparams.Add(e.LocalName, e.InnerText.Trim()); } + var deviceId = "fgd"; + var env = new XmlDocument(); env.AppendChild(env.CreateXmlDeclaration("1.0", "utf-8", "yes")); var envelope = env.CreateElement("SOAP-ENV", "Envelope", NS_SOAPENV); @@ -116,7 +118,7 @@ namespace MediaBrowser.Dlna.Server result = HandleGetSystemUpdateID(); break; case "Browse": - result = HandleBrowse(sparams, user); + result = HandleBrowse(sparams, user, deviceId); break; case "X_GetFeatureList": result = HandleXGetFeatureList(); @@ -235,7 +237,7 @@ namespace MediaBrowser.Dlna.Server return builder.ToString(); } - private IEnumerable> HandleBrowse(Headers sparams, User user) + private IEnumerable> HandleBrowse(Headers sparams, User user, string deviceId) { var id = sparams["ObjectID"]; var flag = sparams["BrowseFlag"]; @@ -298,7 +300,7 @@ namespace MediaBrowser.Dlna.Server } else { - Browse_AddItem(result, i, user); + Browse_AddItem(result, i, user, deviceId); } } } @@ -366,7 +368,7 @@ namespace MediaBrowser.Dlna.Server } } - private void Browse_AddItem(XmlDocument result, BaseItem item, User user) + private void Browse_AddItem(XmlDocument result, BaseItem item, User user, string deviceId) { var element = result.CreateElement(string.Empty, "item", NS_DIDL); element.SetAttribute("restricted", "1"); @@ -389,13 +391,13 @@ namespace MediaBrowser.Dlna.Server var audio = item as Audio; if (audio != null) { - AddAudioResource(element, audio); + AddAudioResource(element, audio, deviceId); } var video = item as Video; if (video != null) { - AddVideoResource(element, video); + AddVideoResource(element, video, deviceId); } AddCover(item, element); @@ -403,12 +405,7 @@ namespace MediaBrowser.Dlna.Server result.DocumentElement.AppendChild(element); } - private string GetDeviceId() - { - return "erer"; - } - - private void AddVideoResource(XmlElement container, Video video) + private void AddVideoResource(XmlElement container, Video video, string deviceId) { var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL); @@ -421,7 +418,7 @@ namespace MediaBrowser.Dlna.Server ItemId = video.Id.ToString("N"), MediaSources = sources, Profile = _profile, - DeviceId = GetDeviceId(), + DeviceId = deviceId, MaxBitrate = maxBitrateSetting }); @@ -435,17 +432,21 @@ namespace MediaBrowser.Dlna.Server res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture)); } - if (streamInfo.IsDirectStream && mediaSource.Size.HasValue) + if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength) { - res.SetAttribute("size", mediaSource.Size.Value.ToString(_usCulture)); + var size = streamInfo.TargetSize; + + if (size.HasValue) + { + res.SetAttribute("size", size.Value.ToString(_usCulture)); + } } var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video && !string.Equals(i.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)); - var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio); - var targetAudioBitrate = streamInfo.AudioBitrate ?? (audioStream == null ? null : audioStream.BitRate); - var targetSampleRate = audioStream == null ? null : audioStream.SampleRate; - var targetChannels = streamInfo.MaxAudioChannels ?? (audioStream == null ? null : audioStream.Channels); + var targetAudioBitrate = streamInfo.TargetAudioBitrate; + var targetSampleRate = streamInfo.TargetAudioSampleRate; + var targetChannels = streamInfo.TargetAudioChannels; var targetWidth = streamInfo.MaxWidth ?? (videoStream == null ? null : videoStream.Width); var targetHeight = streamInfo.MaxHeight ?? (videoStream == null ? null : videoStream.Height); @@ -454,9 +455,7 @@ namespace MediaBrowser.Dlna.Server ? (videoStream == null ? null : videoStream.Codec) : streamInfo.VideoCodec; - var targetAudioCodec = streamInfo.IsDirectStream - ? (audioStream == null ? null : audioStream.Codec) - : streamInfo.AudioCodec; + var targetAudioCodec = streamInfo.TargetAudioCodec; var targetBitrate = maxBitrateSetting ?? mediaSource.Bitrate; @@ -506,7 +505,7 @@ namespace MediaBrowser.Dlna.Server container.AppendChild(res); } - private void AddAudioResource(XmlElement container, Audio audio) + private void AddAudioResource(XmlElement container, Audio audio, string deviceId) { var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL); @@ -517,7 +516,7 @@ namespace MediaBrowser.Dlna.Server ItemId = audio.Id.ToString("N"), MediaSources = sources, Profile = _profile, - DeviceId = GetDeviceId() + DeviceId = deviceId }); var url = streamInfo.ToDlnaUrl(_serverAddress); @@ -530,16 +529,19 @@ namespace MediaBrowser.Dlna.Server res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture)); } - if (streamInfo.IsDirectStream && mediaSource.Size.HasValue) + if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength) { - res.SetAttribute("size", mediaSource.Size.Value.ToString(_usCulture)); + var size = streamInfo.TargetSize; + + if (size.HasValue) + { + res.SetAttribute("size", size.Value.ToString(_usCulture)); + } } - var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio); - - var targetAudioBitrate = streamInfo.AudioBitrate ?? (audioStream == null ? null : audioStream.BitRate); - var targetSampleRate = audioStream == null ? null : audioStream.SampleRate; - var targetChannels = streamInfo.MaxAudioChannels ?? (audioStream == null ? null : audioStream.Channels); + var targetAudioBitrate = streamInfo.TargetAudioBitrate; + var targetSampleRate = streamInfo.TargetAudioSampleRate; + var targetChannels = streamInfo.TargetAudioChannels; if (targetChannels.HasValue) { diff --git a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs b/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs index 33286a474..4d3ccf9a0 100644 --- a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs +++ b/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Dlna.Server private void AppendDeviceProperties(StringBuilder builder) { - builder.Append("" + SecurityElement.Escape(_serverUdn) + ""); + builder.Append("uuid:" + SecurityElement.Escape(_serverUdn) + ""); builder.Append("" + SecurityElement.Escape(_profile.XDlnaCap ?? string.Empty) + ""); if (!string.IsNullOrWhiteSpace(_profile.XDlnaDoc)) diff --git a/MediaBrowser.Dlna/Server/DlnaServerEntryPoint.cs b/MediaBrowser.Dlna/Server/DlnaServerEntryPoint.cs index 050d89f49..d7a34c699 100644 --- a/MediaBrowser.Dlna/Server/DlnaServerEntryPoint.cs +++ b/MediaBrowser.Dlna/Server/DlnaServerEntryPoint.cs @@ -1,11 +1,11 @@ -using System.Linq; -using MediaBrowser.Common; +using MediaBrowser.Common; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Logging; using System; +using System.Linq; using System.Net; namespace MediaBrowser.Dlna.Server diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 932d5d63d..ca5f569ed 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -222,6 +222,8 @@ namespace MediaBrowser.Model.Configuration public DlnaOptions DlnaOptions { get; set; } + public double DownMixAudioBoost { get; set; } + /// /// Initializes a new instance of the class. /// @@ -242,6 +244,7 @@ namespace MediaBrowser.Model.Configuration EnablePeoplePrefixSubFolders = true; EnableUPnP = true; + DownMixAudioBoost = 2; MinResumePct = 5; MaxResumePct = 90; diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs index 76480930f..8b5966c9a 100644 --- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs +++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs @@ -35,17 +35,23 @@ namespace MediaBrowser.Model.Dlna private MediaFormatProfile ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, int? bitrate, TransportStreamTimestamp timestampType) { - // String suffix = ""; - // if (isNoTimestamp(timestampType)) - // suffix = "_ISO"; - // else if (timestampType == TransportStreamTimestamp.VALID) { - // suffix = "_T"; - // } + var suffix = ""; - // String resolution = "S"; - // if ((width.intValue() > 720) || (height.intValue() > 576)) { - // resolution = "H"; - // } + 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 (videoCodec == VideoCodec.MPEG2) // { @@ -55,54 +61,60 @@ namespace MediaBrowser.Model.Dlna // profiles.add(MediaFormatProfile.MPEG_TS_JP_T); // } // return profiles; - // }if (videoCodec == VideoCodec.H264) - // { - // if (audioCodec == AudioCodec.LPCM) - // return Collections.singletonList(MediaFormatProfile.AVC_TS_HD_50_LPCM_T); - // if (audioCodec == AudioCodec.DTS) { - // if (isNoTimestamp(timestampType)) { - // return Collections.singletonList(MediaFormatProfile.AVC_TS_HD_DTS_ISO); - // } - // return Collections.singletonList(MediaFormatProfile.AVC_TS_HD_DTS_T); - // } - // if (audioCodec == AudioCodec.MP2) { - // if (isNoTimestamp(timestampType)) { - // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_HP_%sD_MPEG1_L2_ISO", cast(Object[])[ resolution ]))); - // } - // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_HP_%sD_MPEG1_L2_T", cast(Object[])[ resolution ]))); - // } + // } + if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + { + if (string.Equals(audioCodec, "lpcm", StringComparison.OrdinalIgnoreCase)) + return MediaFormatProfile.AVC_TS_HD_50_LPCM_T; - // if (audioCodec == AudioCodec.AAC) - // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_MP_%sD_AAC_MULT5%s", cast(Object[])[ resolution, suffix ]))); - // if (audioCodec == AudioCodec.MP3) - // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_MP_%sD_MPEG1_L3%s", cast(Object[])[ resolution, suffix ]))); - // if ((audioCodec is null) || (audioCodec == AudioCodec.AC3)) { - // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_MP_%sD_AC3%s", cast(Object[])[ resolution, suffix ]))); - // } - // } - // else if (videoCodec == VideoCodec.VC1) { - // if ((audioCodec is null) || (audioCodec == AudioCodec.AC3)) - // { - // if ((width.intValue() > 720) || (height.intValue() > 576)) { - // return Collections.singletonList(MediaFormatProfile.VC1_TS_AP_L2_AC3_ISO); - // } - // return Collections.singletonList(MediaFormatProfile.VC1_TS_AP_L1_AC3_ISO); - // } - // if (audioCodec == AudioCodec.DTS) { - // suffix = suffix.equals("_ISO") ? suffix : "_T"; - // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("VC1_TS_HD_DTS%s", cast(Object[])[ suffix ]))); - // } - // } else if ((videoCodec == VideoCodec.MPEG4) || (videoCodec == VideoCodec.MSMPEG4)) { - // if (audioCodec == AudioCodec.AAC) - // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_AAC%s", cast(Object[])[ suffix ]))); - // if (audioCodec == AudioCodec.MP3) - // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_MPEG1_L3%s", cast(Object[])[ suffix ]))); - // if (audioCodec == AudioCodec.MP2) - // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_MPEG2_L2%s", cast(Object[])[ suffix ]))); - // if ((audioCodec is null) || (audioCodec == AudioCodec.AC3)) { - // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_AC3%s", cast(Object[])[ suffix ]))); - // } - // } + if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase)) + { + if (timestampType == TransportStreamTimestamp.NONE) + { + return MediaFormatProfile.AVC_TS_HD_DTS_ISO; + } + return MediaFormatProfile.AVC_TS_HD_DTS_T; + } + //if (audioCodec == AudioCodec.MP2) { + // if (isNoTimestamp(timestampType)) { + // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_HP_%sD_MPEG1_L2_ISO", cast(Object[])[ resolution ]))); + // } + // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_HP_%sD_MPEG1_L2_T", cast(Object[])[ resolution ]))); + //} + + //if (audioCodec == AudioCodec.AAC) + // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_MP_%sD_AAC_MULT5%s", cast(Object[])[ resolution, suffix ]))); + //if (audioCodec == AudioCodec.MP3) + // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_MP_%sD_MPEG1_L3%s", cast(Object[])[ resolution, suffix ]))); + //if ((audioCodec is null) || (audioCodec == AudioCodec.AC3)) { + // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("AVC_TS_MP_%sD_AC3%s", cast(Object[])[ resolution, suffix ]))); + //} + } + else if (string.Equals(videoCodec, "vc1", StringComparison.OrdinalIgnoreCase)) + { + if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)) + { + if ((width.HasValue && width.Value > 720) || (height.HasValue && height.Value > 576)) + { + return MediaFormatProfile.VC1_TS_AP_L2_AC3_ISO; + } + return MediaFormatProfile.VC1_TS_AP_L1_AC3_ISO; + } + // if (audioCodec == AudioCodec.DTS) { + // suffix = suffix.equals("_ISO") ? suffix : "_T"; + // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("VC1_TS_HD_DTS%s", cast(Object[])[ suffix ]))); + // } + //} else if ((videoCodec == VideoCodec.MPEG4) || (videoCodec == VideoCodec.MSMPEG4)) { + // if (audioCodec == AudioCodec.AAC) + // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_AAC%s", cast(Object[])[ suffix ]))); + // if (audioCodec == AudioCodec.MP3) + // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_MPEG1_L3%s", cast(Object[])[ suffix ]))); + // if (audioCodec == AudioCodec.MP2) + // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_MPEG2_L2%s", cast(Object[])[ suffix ]))); + // if ((audioCodec is null) || (audioCodec == AudioCodec.AC3)) { + // return Collections.singletonList(MediaFormatProfile.valueOf(String.format("MPEG4_P2_TS_ASP_AC3%s", cast(Object[])[ suffix ]))); + // } + } throw new ArgumentException("Mpeg video file does not match any supported DLNA profile"); } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index d975b1c4b..0c007d694 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -81,7 +81,7 @@ namespace MediaBrowser.Model.Dlna { ItemId = options.ItemId, MediaType = DlnaProfileType.Audio, - MediaSourceId = item.Id, + MediaSource = item, RunTimeTicks = item.RunTimeTicks }; @@ -116,6 +116,7 @@ namespace MediaBrowser.Model.Dlna { playlistItem.IsDirectStream = false; playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; + playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength; playlistItem.Container = transcodingProfile.Container; playlistItem.AudioCodec = transcodingProfile.AudioCodec; @@ -152,7 +153,7 @@ namespace MediaBrowser.Model.Dlna { ItemId = options.ItemId, MediaType = DlnaProfileType.Video, - MediaSourceId = item.Id, + MediaSource = item, RunTimeTicks = item.RunTimeTicks }; @@ -196,6 +197,7 @@ namespace MediaBrowser.Model.Dlna { playlistItem.IsDirectStream = false; playlistItem.Container = transcodingProfile.Container; + playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength; playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',').FirstOrDefault(); playlistItem.VideoCodec = transcodingProfile.VideoCodec; diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 6ba7fe399..b46d95af8 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -1,7 +1,9 @@ using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; namespace MediaBrowser.Model.Dlna { @@ -12,8 +14,6 @@ namespace MediaBrowser.Model.Dlna { public string ItemId { get; set; } - public string MediaSourceId { get; set; } - public bool IsDirectStream { get; set; } public DlnaProfileType MediaType { get; set; } @@ -50,6 +50,18 @@ namespace MediaBrowser.Model.Dlna public TranscodeSeekInfo TranscodeSeekInfo { get; set; } + public bool EstimateContentLength { get; set; } + + public MediaSourceInfo MediaSource { get; set; } + + public string MediaSourceId + { + get + { + return MediaSource == null ? null : MediaSource.Id; + } + } + public string ToUrl(string baseUrl) { return ToDlnaUrl(baseUrl); @@ -102,6 +114,136 @@ namespace MediaBrowser.Model.Dlna return string.Format("Params={0}", string.Join(";", list.ToArray())); } + /// + /// Returns the audio stream that will be used + /// + public MediaStream TargetAudioStream + { + get + { + if (MediaSource != null) + { + var audioStreams = MediaSource.MediaStreams.Where(i => i.Type == MediaStreamType.Audio); + + if (AudioStreamIndex.HasValue) + { + return audioStreams.FirstOrDefault(i => i.Index == AudioStreamIndex.Value); + } + + return audioStreams.FirstOrDefault(); + } + + return null; + } + } + + /// + /// Returns the video stream that will be used + /// + public MediaStream TargetVideoStream + { + get + { + if (MediaSource != null) + { + return MediaSource.MediaStreams + .FirstOrDefault(i => i.Type == MediaStreamType.Video && (i.Codec ?? string.Empty).IndexOf("jpeg", StringComparison.OrdinalIgnoreCase) == -1); + } + + return null; + } + } + + /// + /// Predicts the audio sample rate that will be in the output stream + /// + public int? TargetAudioSampleRate + { + get + { + var stream = TargetAudioStream; + return stream == null ? null : stream.SampleRate; + } + } + + /// + /// Predicts the audio bitrate that will be in the output stream + /// + public int? TargetAudioBitrate + { + get + { + var stream = TargetAudioStream; + return AudioBitrate.HasValue && !IsDirectStream + ? AudioBitrate + : stream == null ? null : stream.BitRate; + } + } + + /// + /// Predicts the audio channels that will be in the output stream + /// + public int? TargetAudioChannels + { + get + { + var stream = TargetAudioStream; + + return MaxAudioChannels.HasValue && !IsDirectStream + ? (stream.Channels.HasValue ? Math.Min(MaxAudioChannels.Value, stream.Channels.Value) : MaxAudioChannels.Value) + : stream == null ? null : stream.Channels; + } + } + + /// + /// Predicts the audio codec that will be in the output stream + /// + public string TargetAudioCodec + { + get + { + var stream = TargetAudioStream; + + return IsDirectStream + ? (stream == null ? null : stream.Codec) + : AudioCodec; + } + } + + /// + /// Predicts the audio channels that will be in the output stream + /// + public long? TargetSize + { + get + { + if (IsDirectStream) + { + return MediaSource.Bitrate; + } + + if (RunTimeTicks.HasValue) + { + var totalBitrate = 0; + + if (AudioBitrate.HasValue) + { + totalBitrate += AudioBitrate.Value; + } + if (VideoBitrate.HasValue) + { + totalBitrate += VideoBitrate.Value; + } + + return Convert.ToInt64(totalBitrate * TimeSpan.FromTicks(RunTimeTicks.Value).TotalSeconds); + } + var stream = TargetAudioStream; + + return MaxAudioChannels.HasValue && !IsDirectStream + ? (stream.Channels.HasValue ? Math.Min(MaxAudioChannels.Value, stream.Channels.Value) : MaxAudioChannels.Value) + : stream == null ? null : stream.Channels; + } + } } /// diff --git a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs index 3672afbc2..629d21df6 100644 --- a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs +++ b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs @@ -356,6 +356,7 @@ namespace MediaBrowser.Server.Implementations.Localization new LocalizatonOption{ Name="Greek", Value="el"}, new LocalizatonOption{ Name="Hebrew", Value="he"}, new LocalizatonOption{ Name="Italian", Value="it"}, + new LocalizatonOption{ Name="Kazakh", Value="kk"}, new LocalizatonOption{ Name="Norwegian Bokmål", Value="nb"}, new LocalizatonOption{ Name="Portuguese (Brazil)", Value="pt-BR"}, new LocalizatonOption{ Name="Portuguese (Portugal)", Value="pt-PT"}, diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index d16e0be99..b91cfdd84 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -515,5 +515,7 @@ "ServerUpToDate": "Media Browser Server is up to date", "ErrorConnectingToMediaBrowserRepository": "There was an error connecting to the remote Media Browser repository.", "LabelComponentsUpdated": "The following components have been installed or updated:", - "MessagePleaseRestartServerToFinishUpdating": "Please restart the server to finish applying updates." + "MessagePleaseRestartServerToFinishUpdating": "Please restart the server to finish applying updates.", + "LabelDownMixAudioScale": "Down mix audio boost scale:", + "LabelDownMixAudioScaleHelp": "Boost audio when downmixing. Set to 1 to preserve original volume value." } \ No newline at end of file