From 72bd678b9736ed0cdd8afea90e7e0c91c5b9b4c9 Mon Sep 17 00:00:00 2001 From: tikuf Date: Tue, 25 Mar 2014 15:57:01 +1100 Subject: [PATCH 1/3] Chapter Thumbs - Remove crop & change thumb sample to 20 --- .../MediaEncoder/MediaEncoder.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index 68e9e0c78..02c629906 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -824,33 +824,34 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder // apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600. // This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar - var vf = "crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + var vf = "scale=600:trunc(600/dar/2)*2"; + //crop=min(iw\,ih*dar):min(ih\,iw/dar):(iw-min(iw\,iw*sar))/2:(ih - min (ih\,ih/sar))/2,scale=600:(600/dar),thumbnail" -f image2 if (threedFormat.HasValue) { switch (threedFormat.Value) { case Video3DFormat.HalfSideBySide: - vf = "crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + vf = "crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2"; // hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not. break; case Video3DFormat.FullSideBySide: - vf = "crop=iw/2:ih:0:0,setdar=dar=a,,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + vf = "crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2"; //fsbs crop width in half,set the display aspect,crop out any black bars we may have made the scale width to 600. break; case Video3DFormat.HalfTopAndBottom: - vf = "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + vf = "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2"; //htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600 break; case Video3DFormat.FullTopAndBottom: - vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2"; // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made the scale width to 600 break; } } // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. - var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"thumbnail,{2}\" -f image2 \"{1}\"", inputPath, "-", vf) : + var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=20\" -f image2 \"{1}\"", inputPath, "-", vf) : string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf); var probeSize = GetProbeSizeArgument(type); From f245fffad10bde5bf559e67ab5342df8dd2d0aa9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 25 Mar 2014 01:25:03 -0400 Subject: [PATCH 2/3] implement dlna headers --- .../Playback/BaseStreamingService.cs | 244 ++++++++++++++---- .../Playback/Hls/BaseHlsService.cs | 4 +- .../Playback/Hls/DynamicHlsService.cs | 4 +- .../Playback/Hls/VideoHlsService.cs | 3 +- .../Playback/Progressive/AudioService.cs | 3 +- .../BaseProgressiveStreamingService.cs | 91 +------ .../Playback/Progressive/VideoService.cs | 3 +- MediaBrowser.Api/Playback/StreamState.cs | 14 + .../Dlna/DeviceIdentification.cs | 4 +- MediaBrowser.Controller/Dlna/DeviceProfile.cs | 147 ++++++++++- MediaBrowser.Controller/Dlna/IDlnaManager.cs | 7 + MediaBrowser.Controller/Dlna/MediaProfile.cs | 5 + .../Dlna/TranscodingProfile.cs | 12 +- .../Providers/BaseItemXmlParser.cs | 88 ------- MediaBrowser.Dlna/DlnaManager.cs | 37 ++- MediaBrowser.Dlna/MediaBrowser.Dlna.csproj | 5 +- MediaBrowser.Dlna/PlayTo/Device.cs | 17 +- MediaBrowser.Dlna/PlayTo/DeviceInfo.cs | 25 +- MediaBrowser.Dlna/PlayTo/DidlBuilder.cs | 83 +++--- MediaBrowser.Dlna/PlayTo/DlnaController.cs | 25 +- .../PlayTo/DlnaControllerFactory.cs | 31 --- MediaBrowser.Dlna/PlayTo/PlayToManager.cs | 62 ++--- MediaBrowser.Dlna/PlayTo/PlaylistItem.cs | 2 - .../PlayTo/PlaylistItemFactory.cs | 17 +- MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs | 20 +- MediaBrowser.Dlna/PlayTo/StreamHelper.cs | 6 - MediaBrowser.Dlna/Profiles/DefaultProfile.cs | 3 +- .../Profiles/Foobar2000Profile.cs | 27 ++ .../Profiles/SamsungSmartTvProfile.cs | 7 + .../Profiles/SonyBlurayPlayer2013Profile.cs | 2 + .../Profiles/SonyBlurayPlayerProfile.cs | 2 + MediaBrowser.Dlna/Server/Headers.cs | 2 +- MediaBrowser.Dlna/Server/RawHeaders.cs | 16 -- MediaBrowser.Dlna/Server/SsdpHandler.cs | 6 +- .../{PlayTo => Ssdp}/SsdpHelper.cs | 44 ++-- .../Savers/MovieXmlSaver.cs | 15 +- .../Savers/SeriesXmlSaver.cs | 10 +- .../Savers/XmlSaverHelpers.cs | 28 +- MediaBrowser.Providers/TV/SeriesXmlParser.cs | 2 + .../MediaEncoder/MediaEncoder.cs | 2 +- 40 files changed, 646 insertions(+), 479 deletions(-) delete mode 100644 MediaBrowser.Dlna/PlayTo/DlnaControllerFactory.cs create mode 100644 MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs delete mode 100644 MediaBrowser.Dlna/Server/RawHeaders.cs rename MediaBrowser.Dlna/{PlayTo => Ssdp}/SsdpHelper.cs (57%) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 7dcb06f7b..f65949ac7 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -65,6 +66,7 @@ namespace MediaBrowser.Api.Playback protected IItemRepository ItemRepository { get; private set; } protected ILiveTvManager LiveTvManager { get; private set; } + protected IDlnaManager DlnaManager { get; private set; } /// /// Initializes a new instance of the class. @@ -77,8 +79,9 @@ namespace MediaBrowser.Api.Playback /// The dto service. /// The file system. /// The item repository. - protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager) + protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager) { + DlnaManager = dlnaManager; EncodingManager = encodingManager; LiveTvManager = liveTvManager; ItemRepository = itemRepository; @@ -774,29 +777,24 @@ namespace MediaBrowser.Api.Playback { var codec = request.AudioCodec; - if (!string.IsNullOrEmpty(codec)) + if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) - { - return "aac -strict experimental"; - } - if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) - { - return "libmp3lame"; - } - if (string.Equals(codec, "vorbis", StringComparison.OrdinalIgnoreCase)) - { - return "libvorbis"; - } - if (string.Equals(codec, "wma", StringComparison.OrdinalIgnoreCase)) - { - return "wmav2"; - } - - return codec.ToLower(); + return "aac -strict experimental"; + } + if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) + { + return "libmp3lame"; + } + if (string.Equals(codec, "vorbis", StringComparison.OrdinalIgnoreCase)) + { + return "libvorbis"; + } + if (string.Equals(codec, "wma", StringComparison.OrdinalIgnoreCase)) + { + return "wmav2"; } - return "copy"; + return codec.ToLower(); } /// @@ -1212,96 +1210,85 @@ namespace MediaBrowser.Api.Playback if (i == 0) { - // Device profile name + request.DeviceId = val; } else if (i == 1) { - request.DeviceId = val; + request.MediaSourceId = val; } else if (i == 2) - { - request.MediaSourceId = val; - } - else if (i == 3) { request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } - else if (i == 4) + else if (i == 3) { if (videoRequest != null) { videoRequest.VideoCodec = val; } } - else if (i == 5) + else if (i == 4) { request.AudioCodec = val; } - else if (i == 6) + else if (i == 5) { if (videoRequest != null) { videoRequest.AudioStreamIndex = int.Parse(val, UsCulture); } } - else if (i == 7) + else if (i == 6) { if (videoRequest != null) { videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture); } } - else if (i == 8) + else if (i == 7) { if (videoRequest != null) { videoRequest.VideoBitRate = int.Parse(val, UsCulture); } } - else if (i == 9) + else if (i == 8) { request.AudioBitRate = int.Parse(val, UsCulture); } - else if (i == 10) + else if (i == 9) { request.MaxAudioChannels = int.Parse(val, UsCulture); } - else if (i == 11) + else if (i == 10) { if (videoRequest != null) { videoRequest.MaxWidth = int.Parse(val, UsCulture); } } - else if (i == 12) + else if (i == 11) { if (videoRequest != null) { videoRequest.MaxHeight = int.Parse(val, UsCulture); } } - else if (i == 13) + else if (i == 12) { if (videoRequest != null) { videoRequest.Framerate = int.Parse(val, UsCulture); } } - else if (i == 14) + else if (i == 13) { if (videoRequest != null) { request.StartTimeTicks = long.Parse(val, UsCulture); } } - else if (i == 15) - { - if (videoRequest != null) - { - videoRequest.Profile = val; - } - } - else if (i == 16) + else if (i == 14) { if (videoRequest != null) { @@ -1487,9 +1474,172 @@ namespace MediaBrowser.Api.Playback state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10; state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440; + ApplyDeviceProfileSettings(state); + return state; } + private void ApplyDeviceProfileSettings(StreamState state) + { + var headers = new Dictionary(); + + foreach (var key in Request.Headers.AllKeys) + { + headers[key] = Request.Headers[key]; + } + + var profile = DlnaManager.GetProfile(headers); + + var container = Path.GetExtension(state.RequestedUrl); + + if (string.IsNullOrEmpty(container)) + { + container = Path.GetExtension(GetOutputFilePath(state)); + } + + var audioCodec = state.Request.AudioCodec; + + if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null) + { + audioCodec = state.AudioStream.Codec; + } + + var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec; + + if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null) + { + videoCodec = state.VideoStream.Codec; + } + + var mediaProfile = state.VideoRequest == null ? + profile.GetAudioMediaProfile(container, audioCodec, state.AudioStream) : + profile.GetVideoMediaProfile(container, audioCodec, videoCodec, state.AudioStream, state.VideoStream); + + if (mediaProfile != null) + { + state.MimeType = mediaProfile.MimeType; + state.OrgPn = mediaProfile.OrgPn; + } + + var transcodingProfile = state.VideoRequest == null ? + profile.GetAudioTranscodingProfile(container, audioCodec) : + profile.GetVideoTranscodingProfile(container, audioCodec, videoCodec); + + if (transcodingProfile != null) + { + state.EstimateContentLength = transcodingProfile.EstimateContentLength; + state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; + state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; + + foreach (var setting in transcodingProfile.Settings) + { + switch (setting.Name) + { + case TranscodingSettingType.VideoProfile: + { + if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.Profile)) + { + state.VideoRequest.Profile = setting.Value; + } + break; + } + default: + throw new ArgumentException("Unrecognized TranscodingSettingType"); + } + } + } + } + + + /// + /// Adds the dlna headers. + /// + /// The state. + /// The response headers. + /// if set to true [is statically streamed]. + /// true if XXXX, false otherwise + protected void AddDlnaHeaders(StreamState state, IDictionary responseHeaders, bool isStaticallyStreamed) + { + var timeSeek = GetHeader("TimeSeekRange.dlna.org"); + + if (!string.IsNullOrEmpty(timeSeek)) + { + ResultFactory.ThrowError(406, "Time seek not supported during encoding.", responseHeaders); + return; + } + + var transferMode = GetHeader("transferMode.dlna.org"); + responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode; + responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*"; + + var contentFeatures = string.Empty; + var extension = GetOutputFileExtension(state); + + // 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 + var orgOp = isStaticallyStreamed || state.TranscodeSeekInfo == TranscodeSeekInfo.Bytes ? ";DLNA.ORG_OP=01" : ";DLNA.ORG_OP=00"; + + // 0 = native, 1 = transcoded + var orgCi = isStaticallyStreamed ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1"; + + const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000"; + + if (!string.IsNullOrWhiteSpace(state.OrgPn)) + { + contentFeatures = "DLNA.ORG_PN=" + state.OrgPn; + } + else if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=MP3"; + } + else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=AAC_ISO"; + } + else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=WMABASE"; + } + else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=AVI"; + } + else if (string.Equals(extension, ".mkv", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=MATROSKA"; + } + else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC"; + } + else if (string.Equals(extension, ".mpeg", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL"; + } + else if (string.Equals(extension, ".ts", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL"; + } + //else if (string.Equals(extension, ".wmv", StringComparison.OrdinalIgnoreCase)) + //{ + // contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; + //} + //else if (string.Equals(extension, ".asf", StringComparison.OrdinalIgnoreCase)) + //{ + // // ?? + // contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; + //} + + if (!string.IsNullOrEmpty(contentFeatures)) + { + responseHeaders["contentFeatures.dlna.org"] = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); + } + + foreach (var item in responseHeaders) + { + Request.Response.AddHeader(item.Key, item.Value); + } + } + /// /// Enforces the resolution limit. /// @@ -1605,7 +1755,7 @@ namespace MediaBrowser.Api.Playback return "vorbis"; } - return null; + return "copy"; } /// diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index eb8f415e0..198376d6a 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -2,13 +2,13 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using System; using System.Collections.Generic; @@ -24,7 +24,7 @@ namespace MediaBrowser.Api.Playback.Hls /// public abstract class BaseHlsService : BaseStreamingService { - protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager) + protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) { } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 66e8b0d14..ab0cd8871 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -1,12 +1,12 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using ServiceStack; using System; @@ -60,7 +60,7 @@ namespace MediaBrowser.Api.Playback.Hls public class DynamicHlsService : BaseHlsService { - public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager) + public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) { } diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index a2080995d..1bca4cae9 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -52,7 +53,7 @@ namespace MediaBrowser.Api.Playback.Hls /// public class VideoHlsService : BaseHlsService { - public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager) + public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) { } diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index 4d8d3a581..ca206c012 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; @@ -43,7 +44,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class AudioService : BaseProgressiveStreamingService { - public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, httpClient, imageProcessor) + public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, httpClient, imageProcessor) { } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 9cb989fc2..dad8a51bd 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -1,13 +1,13 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using ServiceStack.Web; using System; @@ -26,8 +26,7 @@ namespace MediaBrowser.Api.Playback.Progressive protected readonly IImageProcessor ImageProcessor; protected readonly IHttpClient HttpClient; - protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IHttpClient httpClient, IImageProcessor imageProcessor) - : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager) + protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) { HttpClient = httpClient; ImageProcessor = imageProcessor; @@ -100,92 +99,6 @@ namespace MediaBrowser.Api.Playback.Progressive return null; } - /// - /// Adds the dlna headers. - /// - /// The state. - /// The response headers. - /// if set to true [is statically streamed]. - /// true if XXXX, false otherwise - private void AddDlnaHeaders(StreamState state, IDictionary responseHeaders, bool isStaticallyStreamed) - { - var timeSeek = GetHeader("TimeSeekRange.dlna.org"); - - if (!string.IsNullOrEmpty(timeSeek)) - { - ResultFactory.ThrowError(406, "Time seek not supported during encoding.", responseHeaders); - return; - } - - var transferMode = GetHeader("transferMode.dlna.org"); - responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode; - responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*"; - - var contentFeatures = string.Empty; - var extension = GetOutputFileExtension(state); - - // 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 - var orgOp = isStaticallyStreamed ? ";DLNA.ORG_OP=01" : ";DLNA.ORG_OP=00"; - - // 0 = native, 1 = transcoded - var orgCi = isStaticallyStreamed ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1"; - - const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000"; - - if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MP3"; - } - else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=AAC_ISO"; - } - else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=WMABASE"; - } - else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=AVI"; - } - else if (string.Equals(extension, ".mkv", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MATROSKA"; - } - else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC"; - } - else if (string.Equals(extension, ".mpeg", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL"; - } - else if (string.Equals(extension, ".ts", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL"; - } - //else if (string.Equals(extension, ".wmv", StringComparison.OrdinalIgnoreCase)) - //{ - // contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; - //} - //else if (string.Equals(extension, ".asf", StringComparison.OrdinalIgnoreCase)) - //{ - // // ?? - // contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; - //} - - - if (!string.IsNullOrEmpty(contentFeatures)) - { - responseHeaders["contentFeatures.dlna.org"] = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); - } - - foreach (var item in responseHeaders) - { - Request.Response.AddHeader(item.Key, item.Value); - } - } - /// /// Gets the type of the transcoding job. /// diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 43dc6f0d4..0fc78f0e3 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; @@ -58,7 +59,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class VideoService : BaseProgressiveStreamingService { - public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, httpClient, imageProcessor) + public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, httpClient, imageProcessor) { } diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index ecc5c93ef..504d7d921 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using System.Collections.Generic; @@ -77,8 +78,21 @@ namespace MediaBrowser.Api.Playback public string InputAudioCodec { get; set; } + public string MimeType { get; set; } + public string OrgPn { get; set; } + + // DLNA Settings + public bool EstimateContentLength { get; set; } + public bool EnableMpegtsM2TsMode { get; set; } + public TranscodeSeekInfo TranscodeSeekInfo { get; set; } + public string GetMimeType(string outputPath) { + if (!string.IsNullOrEmpty(MimeType)) + { + return MimeType; + } + return MimeTypes.GetMimeType(outputPath); } } diff --git a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs index 461c77537..7b8e3a1e7 100644 --- a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs +++ b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs @@ -41,9 +41,7 @@ namespace MediaBrowser.Controller.Dlna /// /// Gets or sets the manufacturer. /// - /// - /// The manufacturer. - /// + /// The manufacturer. public string Manufacturer { get; set; } /// /// Gets or sets the manufacturer URL. diff --git a/MediaBrowser.Controller/Dlna/DeviceProfile.cs b/MediaBrowser.Controller/Dlna/DeviceProfile.cs index f3de1bc34..f34c4bf64 100644 --- a/MediaBrowser.Controller/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Controller/Dlna/DeviceProfile.cs @@ -1,4 +1,7 @@ - +using MediaBrowser.Model.Entities; +using System; +using System.Linq; + namespace MediaBrowser.Controller.Dlna { public class DeviceProfile @@ -9,12 +12,6 @@ namespace MediaBrowser.Controller.Dlna /// The name. public string Name { get; set; } - /// - /// Gets or sets the type of the client. - /// - /// The type of the client. - public string ClientType { get; set; } - /// /// Gets or sets the transcoding profiles. /// @@ -76,5 +73,141 @@ namespace MediaBrowser.Controller.Dlna CodecProfiles = new CodecProfile[] { }; ContainerProfiles = new ContainerProfile[] { }; } + + public TranscodingProfile GetAudioTranscodingProfile(string container, string audioCodec) + { + container = (container ?? string.Empty).TrimStart('.'); + + return TranscodingProfiles.FirstOrDefault(i => + { + if (i.Type != DlnaProfileType.Audio) + { + return false; + } + + if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty)) + { + return false; + } + + return true; + }); + } + + public TranscodingProfile GetVideoTranscodingProfile(string container, string audioCodec, string videoCodec) + { + container = (container ?? string.Empty).TrimStart('.'); + + return TranscodingProfiles.FirstOrDefault(i => + { + if (i.Type != DlnaProfileType.Video) + { + return false; + } + + if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty)) + { + return false; + } + + if (!string.Equals(videoCodec, i.VideoCodec, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return true; + }); + } + + public MediaProfile GetAudioMediaProfile(string container, string audioCodec, MediaStream audioStream) + { + container = (container ?? string.Empty).TrimStart('.'); + + return MediaProfiles.FirstOrDefault(i => + { + if (i.Type != DlnaProfileType.Audio) + { + return false; + } + + var containers = i.GetContainers().ToList(); + if (containers.Count > 0 && !containers.Contains(container)) + { + return false; + } + + var audioCodecs = i.GetAudioCodecs().ToList(); + if (audioCodecs.Count > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty)) + { + return false; + } + + return true; + }); + } + + public MediaProfile GetVideoMediaProfile(string container, string audioCodec, string videoCodec, MediaStream audioStream, MediaStream videoStream) + { + container = (container ?? string.Empty).TrimStart('.'); + + return MediaProfiles.FirstOrDefault(i => + { + if (i.Type != DlnaProfileType.Video) + { + return false; + } + + var containers = i.GetContainers().ToList(); + if (containers.Count > 0 && !containers.Contains(container)) + { + return false; + } + + var audioCodecs = i.GetAudioCodecs().ToList(); + if (audioCodecs.Count > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty)) + { + return false; + } + + var videoCodecs = i.GetVideoCodecs().ToList(); + if (videoCodecs.Count > 0 && !videoCodecs.Contains(videoCodec ?? string.Empty)) + { + return false; + } + + return true; + }); + } + + public MediaProfile GetPhotoMediaProfile(string container) + { + container = (container ?? string.Empty).TrimStart('.'); + + return MediaProfiles.FirstOrDefault(i => + { + if (i.Type != DlnaProfileType.Photo) + { + return false; + } + + var containers = i.GetContainers().ToList(); + if (containers.Count > 0 && !containers.Contains(container)) + { + return false; + } + + return true; + }); + } } } diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs index 6de17e551..22d13fc3a 100644 --- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs +++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs @@ -16,6 +16,13 @@ namespace MediaBrowser.Controller.Dlna /// DlnaProfile. DeviceProfile GetDefaultProfile(); + /// + /// Gets the profile. + /// + /// The headers. + /// DeviceProfile. + DeviceProfile GetProfile(IDictionary headers); + /// /// Gets the profile. /// diff --git a/MediaBrowser.Controller/Dlna/MediaProfile.cs b/MediaBrowser.Controller/Dlna/MediaProfile.cs index 1d2613fac..9a9b56ddd 100644 --- a/MediaBrowser.Controller/Dlna/MediaProfile.cs +++ b/MediaBrowser.Controller/Dlna/MediaProfile.cs @@ -19,6 +19,11 @@ namespace MediaBrowser.Controller.Dlna { Conditions = new ProfileCondition[] {}; } + + public List GetContainers() + { + return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); + } public List GetAudioCodecs() { diff --git a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs index 007cb632e..d4cfae989 100644 --- a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs @@ -1,4 +1,6 @@ - +using System.Collections.Generic; +using System.Linq; + namespace MediaBrowser.Controller.Dlna { public class TranscodingProfile @@ -11,7 +13,7 @@ namespace MediaBrowser.Controller.Dlna public string AudioCodec { get; set; } public bool EstimateContentLength { get; set; } - + public bool EnableMpegtsM2TsMode { get; set; } public TranscodeSeekInfo TranscodeSeekInfo { get; set; } public TranscodingSetting[] Settings { get; set; } @@ -21,7 +23,11 @@ namespace MediaBrowser.Controller.Dlna Settings = new TranscodingSetting[] { }; } - public bool EnableMpegtsM2TsMode { get; set; } + + public List GetAudioCodecs() + { + return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); + } } public class TranscodingSetting diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index d71c7af32..70b49efec 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -1,5 +1,4 @@ using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -284,22 +283,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "TagLine": - { - var tagline = reader.ReadElementContentAsString(); - - var hasTaglines = item as IHasTaglines; - if (hasTaglines != null) - { - if (!string.IsNullOrWhiteSpace(tagline)) - { - hasTaglines.AddTagline(tagline); - } - } - - break; - } - case "Language": { var val = reader.ReadElementContentAsString(); @@ -380,9 +363,7 @@ namespace MediaBrowser.Controller.Providers } case "ContentRating": - case "certification": case "MPAARating": - case "ESRBRating": { var rating = reader.ReadElementContentAsString(); @@ -415,7 +396,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "Runtime": case "RunningTime": { var text = reader.ReadElementContentAsString(); @@ -431,19 +411,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "Genre": - { - foreach (var name in SplitNames(reader.ReadElementContentAsString())) - { - if (string.IsNullOrWhiteSpace(name)) - { - continue; - } - item.AddGenre(name); - } - break; - } - case "AspectRatio": { var val = reader.ReadElementContentAsString(); @@ -587,7 +554,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "ReleaseYear": case "ProductionYear": { var val = reader.ReadElementContentAsString(); @@ -606,7 +572,6 @@ namespace MediaBrowser.Controller.Providers case "Rating": case "IMDBrating": - case "TGDBRating": { var rating = reader.ReadElementContentAsString(); @@ -683,22 +648,6 @@ namespace MediaBrowser.Controller.Providers } break; } - case "MusicbrainzId": - { - var mbz = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(mbz)) - { - if (item is MusicAlbum) - { - item.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbz); - } - else if (item is MusicArtist) - { - item.SetProviderId(MetadataProviders.MusicBrainzArtist, mbz); - } - } - break; - } case "MusicBrainzAlbumId": { var mbz = reader.ReadElementContentAsString(); @@ -802,9 +751,7 @@ namespace MediaBrowser.Controller.Providers } break; - case "IMDB_ID": case "IMDB": - case "IMDbId": var imDbId = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(imDbId)) { @@ -856,15 +803,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "ParentalRating": - { - using (var subtree = reader.ReadSubtree()) - { - FetchFromParentalRatingNode(subtree, item); - } - break; - } - case "Studios": { using (var subtree = reader.ReadSubtree()) @@ -1227,32 +1165,6 @@ namespace MediaBrowser.Controller.Providers } } - /// - /// Fetches from parental rating node. - /// - /// The reader. - /// The item. - private void FetchFromParentalRatingNode(XmlReader reader, T item) - { - reader.MoveToContent(); - - while (reader.Read()) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - // Removed support for "Value" tag as it conflicted with MPAA rating but leaving this function for possible - // future support of "Description" -ebr - - default: - reader.Skip(); - break; - } - } - } - } - /// /// Gets the persons from XML node. /// diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index c6da865cd..78876d239 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.Dlna; using MediaBrowser.Dlna.Profiles; using MediaBrowser.Model.Serialization; +using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; @@ -43,7 +44,8 @@ namespace MediaBrowser.Dlna new WdtvLiveProfile(), new DenonAvrProfile(), new LinksysDMA2100Profile(), - new LgTvProfile() + new LgTvProfile(), + new Foobar2000Profile() }; foreach (var item in list) @@ -124,5 +126,38 @@ namespace MediaBrowser.Dlna return true; } + + public DeviceProfile GetProfile(IDictionary headers) + { + return GetProfiles().FirstOrDefault(i => IsMatch(headers, i.Identification)) ?? + GetDefaultProfile(); + } + + private bool IsMatch(IDictionary headers, DeviceIdentification profileInfo) + { + return profileInfo.Headers.Any(i => IsMatch(headers, i)); + } + + private bool IsMatch(IDictionary headers, HttpHeaderInfo header) + { + string value; + + if (headers.TryGetValue(header.Name, out value)) + { + switch (header.Match) + { + case HeaderMatchType.Equals: + return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase); + case HeaderMatchType.Substring: + return value.IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1; + case HeaderMatchType.Regex: + return Regex.IsMatch(value, header.Value, RegexOptions.IgnoreCase); + default: + throw new ArgumentException("Unrecognized HeaderMatchType"); + } + } + + return false; + } } } \ No newline at end of file diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index bea281b61..bdfcae39b 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -61,7 +61,6 @@ - Code @@ -70,7 +69,8 @@ - + + @@ -100,7 +100,6 @@ - diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index 2b43c019c..a677cf5dd 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -607,7 +607,7 @@ namespace MediaBrowser.Dlna.PlayTo url = "/" + url; var httpClient = new SsdpHttpClient(_httpClient, _config); - var document = await httpClient.GetDataAsync(new Uri(Properties.BaseUrl + url)); + var document = await httpClient.GetDataAsync(Properties.BaseUrl + url); AvCommands = TransportCommands.Create(document); } @@ -625,7 +625,7 @@ namespace MediaBrowser.Dlna.PlayTo url = "/" + url; var httpClient = new SsdpHttpClient(_httpClient, _config); - var document = await httpClient.GetDataAsync(new Uri(Properties.BaseUrl + url)); + var document = await httpClient.GetDataAsync(Properties.BaseUrl + url); RendererCommands = TransportCommands.Create(document); } @@ -646,7 +646,7 @@ namespace MediaBrowser.Dlna.PlayTo { var ssdpHttpClient = new SsdpHttpClient(httpClient, config); - var document = await ssdpHttpClient.GetDataAsync(url).ConfigureAwait(false); + var document = await ssdpHttpClient.GetDataAsync(url.ToString()).ConfigureAwait(false); var deviceProperties = new DeviceInfo(); @@ -681,10 +681,18 @@ namespace MediaBrowser.Dlna.PlayTo var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault(); if (presentationUrl != null) deviceProperties.PresentationUrl = presentationUrl.Value; + var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault(); if (modelUrl != null) deviceProperties.ModelUrl = modelUrl.Value; - + + var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault(); + if (serialNumber != null) + deviceProperties.SerialNumber = serialNumber.Value; + + var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault(); + if (modelDescription != null) + deviceProperties.ModelDescription = modelDescription.Value; deviceProperties.BaseUrl = String.Format("http://{0}:{1}", url.Host, url.Port); @@ -724,7 +732,6 @@ namespace MediaBrowser.Dlna.PlayTo if (isRenderer) { - var device = new Device(deviceProperties, httpClient, logger, config); await device.GetRenderingProtocolAsync().ConfigureAwait(false); diff --git a/MediaBrowser.Dlna/PlayTo/DeviceInfo.cs b/MediaBrowser.Dlna/PlayTo/DeviceInfo.cs index c57e95c19..122549c7d 100644 --- a/MediaBrowser.Dlna/PlayTo/DeviceInfo.cs +++ b/MediaBrowser.Dlna/PlayTo/DeviceInfo.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Dlna; +using System.Collections.Generic; namespace MediaBrowser.Dlna.PlayTo { @@ -17,27 +17,18 @@ namespace MediaBrowser.Dlna.PlayTo public string ClientType { get; set; } - private string _displayName = string.Empty; - public string DisplayName - { - get - { - return string.IsNullOrEmpty(_displayName) ? Name : _displayName; - } - set - { - _displayName = value; - } - } - public string ModelName { get; set; } public string ModelNumber { get; set; } + public string ModelDescription { get; set; } + public string ModelUrl { get; set; } public string Manufacturer { get; set; } + public string SerialNumber { get; set; } + public string ManufacturerUrl { get; set; } public string PresentationUrl { get; set; } @@ -75,7 +66,9 @@ namespace MediaBrowser.Dlna.PlayTo ModelNumber = ModelNumber, FriendlyName = Name, ManufacturerUrl = ManufacturerUrl, - ModelUrl = ModelUrl + ModelUrl = ModelUrl, + ModelDescription = ModelDescription, + SerialNumber = SerialNumber }; } } diff --git a/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs b/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs index 04f9a4644..1327da148 100644 --- a/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs +++ b/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs @@ -9,31 +9,27 @@ namespace MediaBrowser.Dlna.PlayTo { internal class DidlBuilder { - #region Constants + const string CRLF = "\r\n"; + const string UNKNOWN = "Unknown"; - internal const string CRLF = "\r\n"; - internal const string UNKNOWN = "Unknown"; - - internal const string DIDL_START = @"" + CRLF; - internal const string DIDL_TITLE = @" {0}" + CRLF; - internal const string DIDL_ARTIST = @"{0}" + CRLF; - internal const string DIDL_ALBUM = @"{0}" + CRLF; - internal const string DIDL_TRACKNUM = @"0" + CRLF; - internal const string DIDL_VIDEOCLASS = @" object.item.videoItem" + CRLF; - internal const string DIDL_AUDIOCLASS = @" object.item.audioItem.musicTrack" + CRLF; - internal const string DIDL_IMAGE = @" {0}" + CRLF + - @" {0}" + CRLF; - internal const string DIDL_RELEASEDATE = @" {0}" + CRLF; - internal const string DIDL_GENRE = @" {0}" + CRLF; - internal const string DESCRIPTION = @" {0}" + CRLF; - internal const string DIDL_VIDEO_RES = @" {4}" + CRLF; - internal const string DIDL_AUDIO_RES = @" {3}" + CRLF; - internal const string DIDL_IMAGE_RES = @" {0}" + CRLF; - internal const string DIDL_ALBUMIMAGE_RES = @" {0}" + CRLF; - internal const string DIDL_RATING = @" {0}" + CRLF; - internal const string DIDL_END = ""; - - #endregion + const string DIDL_START = @"" + CRLF; + const string DIDL_TITLE = @" {0}" + CRLF; + const string DIDL_ARTIST = @"{0}" + CRLF; + const string DIDL_ALBUM = @"{0}" + CRLF; + const string DIDL_TRACKNUM = @"{0}" + CRLF; + const string DIDL_VIDEOCLASS = @" object.item.videoItem" + CRLF; + const string DIDL_AUDIOCLASS = @" object.item.audioItem.musicTrack" + CRLF; + const string DIDL_IMAGE = @" {0}" + CRLF + + @" {0}" + CRLF; + const string DIDL_RELEASEDATE = @" {0}" + CRLF; + const string DIDL_GENRE = @" {0}" + CRLF; + const string DESCRIPTION = @" {0}" + CRLF; + const string DIDL_VIDEO_RES = @" {4}" + CRLF; + const string DIDL_AUDIO_RES = @" {3}" + CRLF; + const string DIDL_IMAGE_RES = @" {0}" + CRLF; + const string DIDL_ALBUMIMAGE_RES = @" {0}" + CRLF; + const string DIDL_RATING = @" {0}" + CRLF; + const string DIDL_END = ""; /// /// Builds a Didl MetaData object for the specified dto. @@ -44,7 +40,7 @@ namespace MediaBrowser.Dlna.PlayTo /// The stream URL. /// The streams. /// System.String. - internal static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable streams) + public static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable streams) { string response = string.Format(DIDL_START, dto.Id, userId); response += string.Format(DIDL_TITLE, dto.Name.Replace("&", "and")); @@ -53,7 +49,12 @@ namespace MediaBrowser.Dlna.PlayTo else response += DIDL_AUDIOCLASS; - response += string.Format(DIDL_IMAGE, GetImageUrl(dto, serverAddress)); + var imageUrl = GetImageUrl(dto, serverAddress); + + if (!string.IsNullOrEmpty(imageUrl)) + { + response += string.Format(DIDL_IMAGE, imageUrl); + } response += string.Format(DIDL_RELEASEDATE, GetDateString(dto.PremiereDate)); //TODO Add genres to didl; @@ -63,7 +64,11 @@ namespace MediaBrowser.Dlna.PlayTo { response += string.Format(DESCRIPTION, UNKNOWN); response += GetVideoDIDL(dto, streamUrl, streams); - response += string.Format(DIDL_IMAGE_RES, GetImageUrl(dto, serverAddress)); + + if (!string.IsNullOrEmpty(imageUrl)) + { + response += string.Format(DIDL_IMAGE_RES, imageUrl); + } } else { @@ -74,25 +79,25 @@ namespace MediaBrowser.Dlna.PlayTo response += string.Format(DIDL_ARTIST, audio.Artists.FirstOrDefault() ?? UNKNOWN); response += string.Format(DIDL_ALBUM, audio.Album); - // TODO: Bad format string? response += string.Format(DIDL_TRACKNUM, audio.IndexNumber ?? 0); } response += GetAudioDIDL(dto, streamUrl, streams); - response += string.Format(DIDL_ALBUMIMAGE_RES, GetImageUrl(dto, serverAddress)); + + if (!string.IsNullOrEmpty(imageUrl)) + { + response += string.Format(DIDL_ALBUMIMAGE_RES, imageUrl); + } } response += DIDL_END; return response; - } - #region Private methods - private static string GetVideoDIDL(BaseItem dto, string streamUrl, IEnumerable streams) { - var videostream = streams.Where(stream => stream.Type == Model.Entities.MediaStreamType.Video).OrderBy(s => s.IsDefault).FirstOrDefault(); + var videostream = streams.Where(stream => stream.Type == MediaStreamType.Video).OrderBy(s => s.IsDefault ? 0 : 1).FirstOrDefault(); if (videostream == null) { @@ -105,7 +110,7 @@ namespace MediaBrowser.Dlna.PlayTo private static string GetAudioDIDL(BaseItem dto, string streamUrl, IEnumerable streams) { - var audiostream = streams.Where(stream => stream.Type == MediaStreamType.Audio).OrderBy(s => s.IsDefault).FirstOrDefault(); + var audiostream = streams.Where(stream => stream.Type == MediaStreamType.Audio).OrderBy(s => s.IsDefault ? 0 : 1).FirstOrDefault(); if (audiostream == null) { @@ -118,14 +123,14 @@ namespace MediaBrowser.Dlna.PlayTo private static string GetImageUrl(BaseItem dto, string serverAddress) { - var imageType = ImageType.Primary; + const ImageType imageType = ImageType.Primary; - if (!dto.HasImage(ImageType.Primary)) + if (!dto.HasImage(imageType)) { - dto = dto.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary)); + dto = dto.Parents.FirstOrDefault(i => i.HasImage(imageType)); } - return string.Format("{0}/Items/{1}/Images/{2}", serverAddress, dto.Id, imageType); + return dto == null ? null : string.Format("{0}/Items/{1}/Images/{2}", serverAddress, dto.Id, imageType); } private static string GetDurationString(BaseItem dto) @@ -148,7 +153,5 @@ namespace MediaBrowser.Dlna.PlayTo { return string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); } - - #endregion } } diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs index ecda07f0b..0b0c03fcd 100644 --- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs +++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs @@ -140,8 +140,15 @@ namespace MediaBrowser.Dlna.PlayTo { _updateTimer.Change(Timeout.Infinite, Timeout.Infinite); - //Session is inactive, mark it for Disposal and don't start the elapsed timer. - await _sessionManager.ReportSessionEnded(_session.Id); + try + { + // Session is inactive, mark it for Disposal and don't start the elapsed timer. + await _sessionManager.ReportSessionEnded(_session.Id); + } + catch (Exception ex) + { + _logger.ErrorException("Error in ReportSessionEnded", ex); + } } } @@ -156,7 +163,15 @@ namespace MediaBrowser.Dlna.PlayTo if (!_playbackStarted) { - await _sessionManager.OnPlaybackStart(new PlaybackInfo { Item = _currentItem, SessionId = _session.Id, CanSeek = true, QueueableMediaTypes = new List { "Audio", "Video" } }).ConfigureAwait(false); + await _sessionManager.OnPlaybackStart(new PlaybackInfo + { + Item = _currentItem, + SessionId = _session.Id, + CanSeek = true, + QueueableMediaTypes = new List { "Audio", "Video" } + + }).ConfigureAwait(false); + _playbackStarted = true; } @@ -403,7 +418,6 @@ namespace MediaBrowser.Dlna.PlayTo var playlistItem = GetPlaylistItem(item, streams, profile); playlistItem.StartPositionTicks = startPostionTicks; - playlistItem.DeviceProfileName = profile.Name; if (playlistItem.MediaType == DlnaProfileType.Audio) { @@ -414,8 +428,7 @@ namespace MediaBrowser.Dlna.PlayTo playlistItem.StreamUrl = StreamHelper.GetVideoUrl(_device.Properties, playlistItem, streams, serverAddress); } - var didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams); - playlistItem.Didl = didl; + playlistItem.Didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams); return playlistItem; } diff --git a/MediaBrowser.Dlna/PlayTo/DlnaControllerFactory.cs b/MediaBrowser.Dlna/PlayTo/DlnaControllerFactory.cs deleted file mode 100644 index 720dc200b..000000000 --- a/MediaBrowser.Dlna/PlayTo/DlnaControllerFactory.cs +++ /dev/null @@ -1,31 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Dlna.PlayTo -{ - public class PlayToControllerFactory : ISessionControllerFactory - { - private readonly ISessionManager _sessionManager; - private readonly IItemRepository _itemRepository; - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - private readonly INetworkManager _networkManager; - - public PlayToControllerFactory(ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogManager logManager, INetworkManager networkManager) - { - _itemRepository = itemRepository; - _sessionManager = sessionManager; - _libraryManager = libraryManager; - _networkManager = networkManager; - _logger = logManager.GetLogger("PlayTo"); - } - - public ISessionController GetSessionController(SessionInfo session) - { - return null; - } - } -} diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs index ca76116fe..297f7a696 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs @@ -5,16 +5,17 @@ using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Session; +using MediaBrowser.Dlna.Ssdp; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Session; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -126,10 +127,9 @@ namespace MediaBrowser.Dlna.PlayTo if (receivedBytes > 0) { - var rawData = Encoding.UTF8.GetString(receiveBuffer, 0, receivedBytes); - var uri = SsdpHelper.ParseSsdpResponse(rawData); + var headers = SsdpHelper.ParseSsdpResponse(receiveBuffer); - TryCreateController(uri); + TryCreateController(headers); } } @@ -146,13 +146,20 @@ namespace MediaBrowser.Dlna.PlayTo }, _tokenSource.Token, TaskCreationOptions.LongRunning); } - private void TryCreateController(Uri uri) + private void TryCreateController(IDictionary headers) { + string location; + + if (!headers.TryGetValue("Location", out location)) + { + return; + } + Task.Run(async () => { try { - await CreateController(uri).ConfigureAwait(false); + await CreateController(new Uri(location)).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -221,46 +228,25 @@ namespace MediaBrowser.Dlna.PlayTo if (device != null && device.RendererCommands != null && !_sessionManager.Sessions.Any(s => string.Equals(s.DeviceId, device.Properties.UUID) && s.IsActive)) { - GetProfileSettings(device.Properties); - - var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, device.Properties.Name, device.Properties.UUID, device.Properties.DisplayName, uri.OriginalString, null) + var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null) .ConfigureAwait(false); - _sessionManager.ReportCapabilities(sessionInfo.Id, new SessionCapabilities - { - PlayableMediaTypes = new[] { MediaType.Audio, MediaType.Video, MediaType.Photo }, - SupportsFullscreenToggle = false - }); - var controller = sessionInfo.SessionController as PlayToController; if (controller == null) { sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager, _userManager, _appHost); + + controller.Init(device); + + _sessionManager.ReportCapabilities(sessionInfo.Id, new SessionCapabilities + { + PlayableMediaTypes = new[] { MediaType.Audio, MediaType.Video, MediaType.Photo }, + SupportsFullscreenToggle = false + }); + + _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName); } - - controller.Init(device); - - _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName); - } - } - - /// - /// Gets the profile settings. - /// - /// The device properties. - /// The TranscodeSettings for the device - private void GetProfileSettings(DeviceInfo deviceProperties) - { - var profile = _dlnaManager.GetProfile(deviceProperties.ToDeviceIdentification()); - - if (!string.IsNullOrWhiteSpace(profile.Name)) - { - deviceProperties.DisplayName = profile.Name; - } - if (!string.IsNullOrWhiteSpace(profile.ClientType)) - { - deviceProperties.ClientType = profile.ClientType; } } diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs index 20f31cf9d..e1eea0824 100644 --- a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs +++ b/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs @@ -33,8 +33,6 @@ namespace MediaBrowser.Dlna.PlayTo public int? SubtitleStreamIndex { get; set; } - public string DeviceProfileName { get; set; } - public int? MaxAudioChannels { get; set; } public int? AudioBitrate { get; set; } diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs index 1b2d79113..0dec9bbf3 100644 --- a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs +++ b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs @@ -162,7 +162,8 @@ namespace MediaBrowser.Dlna.PlayTo private void ApplyTranscodingConditions(PlaylistItem item, IEnumerable conditions) { - foreach (var condition in conditions.Where(i => !string.IsNullOrEmpty(i.Value))) + foreach (var condition in conditions + .Where(i => !string.IsNullOrEmpty(i.Value))) { var value = condition.Value; @@ -170,7 +171,7 @@ namespace MediaBrowser.Dlna.PlayTo { case ProfileConditionValue.AudioBitrate: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.AudioBitrate = num; @@ -179,7 +180,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.AudioChannels: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.MaxAudioChannels = num; @@ -199,7 +200,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.Height: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.MaxHeight = num; @@ -208,7 +209,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.VideoBitrate: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.VideoBitrate = num; @@ -217,7 +218,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.VideoFramerate: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.MaxFramerate = num; @@ -226,7 +227,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.VideoLevel: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.VideoLevel = num; @@ -235,7 +236,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.Width: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.MaxWidth = num; diff --git a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs b/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs index f540a8004..b1ae21a43 100644 --- a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs +++ b/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs @@ -2,7 +2,6 @@ using MediaBrowser.Controller.Configuration; using System; using System.IO; -using System.Net; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; @@ -14,8 +13,6 @@ namespace MediaBrowser.Dlna.PlayTo private const string USERAGENT = "Microsoft-Windows/6.2 UPnP/1.0 Microsoft-DLNA DLNADOC/1.50"; private const string FriendlyName = "MediaBrowser"; - private static readonly CookieContainer Container = new CookieContainer(); - private readonly IHttpClient _httpClient; private readonly IServerConfigurationManager _config; @@ -31,7 +28,7 @@ namespace MediaBrowser.Dlna.PlayTo if (!serviceUrl.StartsWith("/")) serviceUrl = "/" + serviceUrl; - var response = await PostSoapDataAsync(new Uri(baseUrl + serviceUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header) + var response = await PostSoapDataAsync(baseUrl + serviceUrl, "\"" + service.ServiceType + "#" + command + "\"", postData, header) .ConfigureAwait(false); using (var stream = response.Content) @@ -43,11 +40,11 @@ namespace MediaBrowser.Dlna.PlayTo } } - public async Task SubscribeAsync(Uri url, string ip, int port, string localIp, int eventport, int timeOut = 3600) + public async Task SubscribeAsync(string url, string ip, int port, string localIp, int eventport, int timeOut = 3600) { var options = new HttpRequestOptions { - Url = url.ToString(), + Url = url, UserAgent = USERAGENT, LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging }; @@ -56,7 +53,6 @@ namespace MediaBrowser.Dlna.PlayTo options.RequestHeaders["CALLBACK"] = "<" + localIp + ":" + eventport + ">"; options.RequestHeaders["NT"] = "upnp:event"; options.RequestHeaders["TIMEOUT"] = "Second - " + timeOut; - //request.CookieContainer = Container; using (await _httpClient.Get(options).ConfigureAwait(false)) { @@ -75,24 +71,22 @@ namespace MediaBrowser.Dlna.PlayTo options.RequestHeaders["CALLBACK"] = "<" + localIp + ":" + eventport + ">"; options.RequestHeaders["NT"] = "upnp:event"; options.RequestHeaders["TIMEOUT"] = "Second - 3600"; - //request.CookieContainer = Container; using (await _httpClient.Get(options).ConfigureAwait(false)) { } } - public async Task GetDataAsync(Uri url) + public async Task GetDataAsync(string url) { var options = new HttpRequestOptions { - Url = url.ToString(), + Url = url, UserAgent = USERAGENT, LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging }; options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName; - //request.CookieContainer = Container; using (var stream = await _httpClient.Get(options).ConfigureAwait(false)) { @@ -103,14 +97,14 @@ namespace MediaBrowser.Dlna.PlayTo } } - private Task PostSoapDataAsync(Uri url, string soapAction, string postData, string header = null, int timeOut = 20000) + private Task PostSoapDataAsync(string url, string soapAction, string postData, string header = null) { if (!soapAction.StartsWith("\"")) soapAction = "\"" + soapAction + "\""; var options = new HttpRequestOptions { - Url = url.ToString(), + Url = url, UserAgent = USERAGENT, LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging }; diff --git a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs index 61c3bdd73..a4855c94f 100644 --- a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs +++ b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs @@ -43,15 +43,10 @@ namespace MediaBrowser.Dlna.PlayTo /// private static string BuildDlnaUrl(DeviceInfo deviceProperties, PlaylistItem item) { - var profile = item.TranscodingSettings.Where(i => i.Name == TranscodingSettingType.VideoProfile) - .Select(i => i.Value) - .FirstOrDefault(); - var usCulture = new CultureInfo("en-US"); var list = new List { - item.DeviceProfileName ?? string.Empty, deviceProperties.UUID ?? string.Empty, item.MediaSourceId ?? string.Empty, (!item.Transcode).ToString().ToLower(), @@ -66,7 +61,6 @@ namespace MediaBrowser.Dlna.PlayTo item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(usCulture) : string.Empty, item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(usCulture) : string.Empty, item.StartPositionTicks.ToString(usCulture), - profile ?? string.Empty, item.VideoLevel.HasValue ? item.VideoLevel.Value.ToString(usCulture) : string.Empty }; diff --git a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs index 710f02df2..214b6f814 100644 --- a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs +++ b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs @@ -6,9 +6,10 @@ namespace MediaBrowser.Dlna.Profiles { public DefaultProfile() { + Name = "Generic Device"; + ProtocolInfo = "DLNA"; - ClientType = "DLNA"; Manufacturer = "Media Browser"; ModelDescription = "Media Browser"; ModelName = "Media Browser"; diff --git a/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs b/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs new file mode 100644 index 000000000..877f1a666 --- /dev/null +++ b/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs @@ -0,0 +1,27 @@ +using MediaBrowser.Controller.Dlna; + +namespace MediaBrowser.Dlna.Profiles +{ + public class Foobar2000Profile : DefaultProfile + { + public Foobar2000Profile() + { + Name = "foobar2000"; + + Identification = new DeviceIdentification + { + FriendlyName = @"foobar", + + Headers = new[] + { + new HttpHeaderInfo + { + Name = "User-Agent", + Value = "foobar", + Match = HeaderMatchType.Substring + } + } + }; + } + } +} diff --git a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs index fa6b1201a..fbbb7a594 100644 --- a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs +++ b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs @@ -302,6 +302,13 @@ namespace MediaBrowser.Dlna.Profiles MediaProfiles = new[] { + new MediaProfile + { + Container = "avi", + MimeType = "video/x-msvideo", + Type = DlnaProfileType.Video + }, + new MediaProfile { Container = "mkv", diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs index 49aa47027..cec29b418 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs @@ -6,6 +6,8 @@ namespace MediaBrowser.Dlna.Profiles { public SonyBlurayPlayer2013Profile() { + Name = "Sony Blu-ray Player 2013"; + Identification = new DeviceIdentification { FriendlyName = @"Blu-ray Disc Player", diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs index 512172670..2c678b11f 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs @@ -6,6 +6,8 @@ namespace MediaBrowser.Dlna.Profiles { public SonyBlurayPlayerProfile() { + Name = "Sony Blu-ray Player"; + Identification = new DeviceIdentification { FriendlyName = @"Blu-ray Disc Player", diff --git a/MediaBrowser.Dlna/Server/Headers.cs b/MediaBrowser.Dlna/Server/Headers.cs index 859ae7fbf..2bd1a72f7 100644 --- a/MediaBrowser.Dlna/Server/Headers.cs +++ b/MediaBrowser.Dlna/Server/Headers.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Dlna.Server private readonly Dictionary _dict = new Dictionary(); private readonly static Regex Validator = new Regex(@"^[a-z\d][a-z\d_.-]+$", RegexOptions.Compiled | RegexOptions.IgnoreCase); - protected Headers(bool asIs) + public Headers(bool asIs) { _asIs = asIs; } diff --git a/MediaBrowser.Dlna/Server/RawHeaders.cs b/MediaBrowser.Dlna/Server/RawHeaders.cs deleted file mode 100644 index f57e6b9f3..000000000 --- a/MediaBrowser.Dlna/Server/RawHeaders.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MediaBrowser.Dlna.Server -{ - public class RawHeaders : Headers - { - public RawHeaders() - : base(true) - { - } - } -} diff --git a/MediaBrowser.Dlna/Server/SsdpHandler.cs b/MediaBrowser.Dlna/Server/SsdpHandler.cs index 63c2abbec..64c111819 100644 --- a/MediaBrowser.Dlna/Server/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Server/SsdpHandler.cs @@ -96,7 +96,7 @@ namespace MediaBrowser.Dlna.Server { break; } - var parts = line.Split(new char[] { ':' }, 2); + var parts = line.Split(new[] { ':' }, 2); headers[parts[0]] = parts[1].Trim(); } @@ -148,7 +148,7 @@ namespace MediaBrowser.Dlna.Server private void SendSearchResponse(IPEndPoint endpoint, UpnpDevice dev) { - var headers = new RawHeaders(); + var headers = new Headers(true); headers.Add("CACHE-CONTROL", "max-age = 600"); headers.Add("DATE", DateTime.Now.ToString("R")); headers.Add("EXT", ""); @@ -188,7 +188,7 @@ namespace MediaBrowser.Dlna.Server private void NotifyDevice(UpnpDevice dev, string type, bool sticky) { _logger.Debug("NotifyDevice"); - var headers = new RawHeaders(); + var headers = new Headers(true); headers.Add("HOST", "239.255.255.250:1900"); headers.Add("CACHE-CONTROL", "max-age = 600"); headers.Add("LOCATION", dev.Descriptor.ToString()); diff --git a/MediaBrowser.Dlna/PlayTo/SsdpHelper.cs b/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs similarity index 57% rename from MediaBrowser.Dlna/PlayTo/SsdpHelper.cs rename to MediaBrowser.Dlna/Ssdp/SsdpHelper.cs index d07a7679f..b22db781a 100644 --- a/MediaBrowser.Dlna/PlayTo/SsdpHelper.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs @@ -1,8 +1,9 @@ using System; -using System.Linq; +using System.Collections.Generic; +using System.IO; using System.Text; -namespace MediaBrowser.Dlna.PlayTo +namespace MediaBrowser.Dlna.Ssdp { public class SsdpHelper { @@ -29,28 +30,29 @@ namespace MediaBrowser.Dlna.PlayTo /// /// The data. /// - public static Uri ParseSsdpResponse(string data) + public static Dictionary ParseSsdpResponse(byte[] data) { - var res = (from line in data.Split(new[] { '\r', '\n' }) - where line.ToLowerInvariant().StartsWith("location:") - select line).FirstOrDefault(); + var headers = new Dictionary(StringComparer.OrdinalIgnoreCase); - return !string.IsNullOrEmpty(res) ? new Uri(res.Substring(9).Trim()) : null; - } + using (var reader = new StreamReader(new MemoryStream(data), Encoding.ASCII)) + { + for (var line = reader.ReadLine(); line != null; line = reader.ReadLine()) + { + line = line.Trim(); + if (string.IsNullOrEmpty(line)) + { + break; + } + var parts = line.Split(new[] { ':' }, 2); - /// - /// Parses data into SSDP event. - /// - /// The data. - /// - [Obsolete("Not yet used", true)] - public static string ParseSsdpEvent(string data) - { - var sid = (from line in data.Split(new[] { '\r', '\n' }) - where line.ToLowerInvariant().StartsWith("sid:") - select line).FirstOrDefault(); - - return data; + if (parts.Length == 2) + { + headers[parts[0]] = parts[1].Trim(); + } + } + } + + return headers; } } } diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs index 595793854..9a31fd091 100644 --- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs @@ -3,7 +3,6 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Entities; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -75,16 +74,6 @@ namespace MediaBrowser.Providers.Savers XmlSaverHelpers.AddCommonNodes(video, builder); - if (video.CommunityRating.HasValue) - { - builder.Append("" + SecurityElement.Escape(video.CommunityRating.Value.ToString(UsCulture)) + ""); - } - - if (!string.IsNullOrEmpty(video.Overview)) - { - builder.Append(""); - } - var musicVideo = item as MusicVideo; if (musicVideo != null) @@ -117,8 +106,12 @@ namespace MediaBrowser.Providers.Savers XmlSaverHelpers.Save(builder, xmlFilePath, new List { + // Deprecated. No longer saving in this field. "IMDBrating", + + // Deprecated. No longer saving in this field. "Description", + "Artist", "Album", "TmdbCollectionName" diff --git a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs index e69f2e085..a7ed55e5d 100644 --- a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs @@ -60,11 +60,6 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(tvdb) + ""); } - if (!string.IsNullOrEmpty(item.Name)) - { - builder.Append("" + SecurityElement.Escape(item.Name) + ""); - } - if (series.Status.HasValue) { builder.Append("" + SecurityElement.Escape(series.Status.Value.ToString()) + ""); @@ -111,7 +106,6 @@ namespace MediaBrowser.Providers.Savers XmlSaverHelpers.Save(builder, xmlFilePath, new List { "id", - "SeriesName", "Status", "Network", "Airs_Time", @@ -120,6 +114,10 @@ namespace MediaBrowser.Providers.Savers // Don't preserve old series node "Series", + + "SeriesName", + + // Deprecated. No longer saving in this field. "AnimeSeriesIndex" }); } diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs index 391ab7cfd..6d681197e 100644 --- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs @@ -28,7 +28,10 @@ namespace MediaBrowser.Providers.Savers "AwardSummary", "BirthDate", "Budget", + + // Deprecated. No longer saving in this field. "certification", + "Chapters", "ContentRating", "CustomRating", @@ -40,22 +43,31 @@ namespace MediaBrowser.Providers.Savers "Genres", "Genre", "GamesDbId", + + // Deprecated. No longer saving in this field. "IMDB_ID", + "IMDB", + + // Deprecated. No longer saving in this field. "IMDbId", + "Language", "LocalTitle", "LockData", "LockedFields", "Format3D", "Metascore", + + // Deprecated. No longer saving in this field. "MPAARating", + "MusicBrainzArtistId", "MusicBrainzAlbumArtistId", "MusicBrainzAlbumId", "MusicBrainzReleaseGroupId", - // Old - not used anymore + // Deprecated. No longer saving in this field. "MusicbrainzId", "Overview", @@ -67,15 +79,24 @@ namespace MediaBrowser.Providers.Savers "Revenue", "RottenTomatoesId", "RunningTime", + + // Deprecated. No longer saving in this field. "Runtime", + "SortTitle", "Studios", "Tags", + + // Deprecated. No longer saving in this field. "TagLine", + "Taglines", "TMDbCollectionId", "TMDbId", + + // Deprecated. No longer saving in this field. "Trailer", + "Trailers", "TVcomId", "TvDbId", @@ -207,8 +228,6 @@ namespace MediaBrowser.Providers.Savers if (!string.IsNullOrEmpty(item.OfficialRating)) { builder.Append("" + SecurityElement.Escape(item.OfficialRating) + ""); - builder.Append("" + SecurityElement.Escape(item.OfficialRating) + ""); - builder.Append("" + SecurityElement.Escape(item.OfficialRating) + ""); } builder.Append("" + SecurityElement.Escape(item.DateCreated.ToLocalTime().ToString("G")) + ""); @@ -376,16 +395,13 @@ namespace MediaBrowser.Providers.Savers var timespan = TimeSpan.FromTicks(runTimeTicks.Value); builder.Append("" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + ""); - builder.Append("" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + ""); } var imdb = item.GetProviderId(MetadataProviders.Imdb); if (!string.IsNullOrEmpty(imdb)) { - builder.Append("" + SecurityElement.Escape(imdb) + ""); builder.Append("" + SecurityElement.Escape(imdb) + ""); - builder.Append("" + SecurityElement.Escape(imdb) + ""); } var tmdb = item.GetProviderId(MetadataProviders.Tmdb); diff --git a/MediaBrowser.Providers/TV/SeriesXmlParser.cs b/MediaBrowser.Providers/TV/SeriesXmlParser.cs index 0c220031c..9f68ad7a4 100644 --- a/MediaBrowser.Providers/TV/SeriesXmlParser.cs +++ b/MediaBrowser.Providers/TV/SeriesXmlParser.cs @@ -90,6 +90,8 @@ namespace MediaBrowser.Providers.TV break; } case "SeriesName": + // TODO: Deprecate in mid-2014 + // No longer saving this tag but will still read it for a while item.Name = reader.ReadElementContentAsString(); break; diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index 02c629906..fcfe98a46 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -851,7 +851,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. - var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=20\" -f image2 \"{1}\"", inputPath, "-", vf) : + var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"thumbnail,{2}\" -f image2 \"{1}\"", inputPath, "-", vf) : string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf); var probeSize = GetProbeSizeArgument(type); From b5a3cbfb74e96348ff50fb9fa987d316501d2174 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 25 Mar 2014 01:42:58 -0400 Subject: [PATCH 3/3] restore thumbnail change --- .../MediaEncoder/MediaEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index fcfe98a46..fddba7662 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -851,7 +851,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. - var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"thumbnail,{2}\" -f image2 \"{1}\"", inputPath, "-", vf) : + var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail\" -f image2 \"{1}\"", inputPath, "-", vf) : string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf); var probeSize = GetProbeSizeArgument(type);