Luke Pulverenti преди 11 години
родител
ревизия
f245fffad1
променени са 40 файла, в които са добавени 645 реда и са изтрити 478 реда
  1. 197 47
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  2. 2 2
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  3. 2 2
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  4. 2 1
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  5. 2 1
      MediaBrowser.Api/Playback/Progressive/AudioService.cs
  6. 2 89
      MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
  7. 2 1
      MediaBrowser.Api/Playback/Progressive/VideoService.cs
  8. 14 0
      MediaBrowser.Api/Playback/StreamState.cs
  9. 1 3
      MediaBrowser.Controller/Dlna/DeviceIdentification.cs
  10. 140 7
      MediaBrowser.Controller/Dlna/DeviceProfile.cs
  11. 7 0
      MediaBrowser.Controller/Dlna/IDlnaManager.cs
  12. 5 0
      MediaBrowser.Controller/Dlna/MediaProfile.cs
  13. 9 3
      MediaBrowser.Controller/Dlna/TranscodingProfile.cs
  14. 0 88
      MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
  15. 36 1
      MediaBrowser.Dlna/DlnaManager.cs
  16. 2 3
      MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
  17. 12 5
      MediaBrowser.Dlna/PlayTo/Device.cs
  18. 9 16
      MediaBrowser.Dlna/PlayTo/DeviceInfo.cs
  19. 44 41
      MediaBrowser.Dlna/PlayTo/DidlBuilder.cs
  20. 19 6
      MediaBrowser.Dlna/PlayTo/DlnaController.cs
  21. 0 31
      MediaBrowser.Dlna/PlayTo/DlnaControllerFactory.cs
  22. 22 36
      MediaBrowser.Dlna/PlayTo/PlayToManager.cs
  23. 0 2
      MediaBrowser.Dlna/PlayTo/PlaylistItem.cs
  24. 9 8
      MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
  25. 7 13
      MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs
  26. 0 6
      MediaBrowser.Dlna/PlayTo/StreamHelper.cs
  27. 2 1
      MediaBrowser.Dlna/Profiles/DefaultProfile.cs
  28. 27 0
      MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs
  29. 7 0
      MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
  30. 2 0
      MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs
  31. 2 0
      MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs
  32. 1 1
      MediaBrowser.Dlna/Server/Headers.cs
  33. 0 16
      MediaBrowser.Dlna/Server/RawHeaders.cs
  34. 3 3
      MediaBrowser.Dlna/Server/SsdpHandler.cs
  35. 23 21
      MediaBrowser.Dlna/Ssdp/SsdpHelper.cs
  36. 4 11
      MediaBrowser.Providers/Savers/MovieXmlSaver.cs
  37. 4 6
      MediaBrowser.Providers/Savers/SeriesXmlSaver.cs
  38. 22 6
      MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
  39. 2 0
      MediaBrowser.Providers/TV/SeriesXmlParser.cs
  40. 1 1
      MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs

+ 197 - 47
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; }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
@@ -77,8 +79,9 @@ namespace MediaBrowser.Api.Playback
         /// <param name="dtoService">The dto service.</param>
         /// <param name="fileSystem">The file system.</param>
         /// <param name="itemRepository">The item repository.</param>
-        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();
         }
 
         /// <summary>
@@ -1211,97 +1209,86 @@ namespace MediaBrowser.Api.Playback
                 }
 
                 if (i == 0)
-                {
-                    // Device profile name
-                }
-                else if (i == 1)
                 {
                     request.DeviceId = val;
                 }
-                else if (i == 2)
+                else if (i == 1)
                 {
                     request.MediaSourceId = val;
                 }
-                else if (i == 3)
+                else if (i == 2)
                 {
                     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<string, string>();
+
+            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");
+                    }
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Adds the dlna headers.
+        /// </summary>
+        /// <param name="state">The state.</param>
+        /// <param name="responseHeaders">The response headers.</param>
+        /// <param name="isStaticallyStreamed">if set to <c>true</c> [is statically streamed].</param>
+        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
+        protected void AddDlnaHeaders(StreamState state, IDictionary<string, string> 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);
+            }
+        }
+
         /// <summary>
         /// Enforces the resolution limit.
         /// </summary>
@@ -1605,7 +1755,7 @@ namespace MediaBrowser.Api.Playback
                 return "vorbis";
             }
 
-            return null;
+            return "copy";
         }
 
         /// <summary>

+ 2 - 2
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
     /// </summary>
     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)
         {
         }
 

+ 2 - 2
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)
         {
         }
 

+ 2 - 1
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
     /// </summary>
     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)
         {
         }
 

+ 2 - 1
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
     /// </summary>
     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)
         {
         }
 

+ 2 - 89
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;
         }
 
-        /// <summary>
-        /// Adds the dlna headers.
-        /// </summary>
-        /// <param name="state">The state.</param>
-        /// <param name="responseHeaders">The response headers.</param>
-        /// <param name="isStaticallyStreamed">if set to <c>true</c> [is statically streamed].</param>
-        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        private void AddDlnaHeaders(StreamState state, IDictionary<string, string> 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);
-            }
-        }
-
         /// <summary>
         /// Gets the type of the transcoding job.
         /// </summary>

+ 2 - 1
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
     /// </summary>
     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)
         {
         }
 

+ 14 - 0
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);
         }
     }

+ 1 - 3
MediaBrowser.Controller/Dlna/DeviceIdentification.cs

@@ -41,9 +41,7 @@ namespace MediaBrowser.Controller.Dlna
         /// <summary>
         /// Gets or sets the manufacturer.
         /// </summary>
-        /// <value>
-        /// The manufacturer.
-        /// </value>
+        /// <value>The manufacturer.</value>
         public string Manufacturer { get; set; }
         /// <summary>
         /// Gets or sets the manufacturer URL.

+ 140 - 7
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
         /// <value>The name.</value>
         public string Name { get; set; }
 
-        /// <summary>
-        /// Gets or sets the type of the client.
-        /// </summary>
-        /// <value>The type of the client.</value>
-        public string ClientType { get; set; }
-
         /// <summary>
         /// Gets or sets the transcoding profiles.
         /// </summary>
@@ -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;
+            });
+        }
     }
 }

+ 7 - 0
MediaBrowser.Controller/Dlna/IDlnaManager.cs

@@ -16,6 +16,13 @@ namespace MediaBrowser.Controller.Dlna
         /// <returns>DlnaProfile.</returns>
         DeviceProfile GetDefaultProfile();
 
+        /// <summary>
+        /// Gets the profile.
+        /// </summary>
+        /// <param name="headers">The headers.</param>
+        /// <returns>DeviceProfile.</returns>
+        DeviceProfile GetProfile(IDictionary<string,string> headers);
+
         /// <summary>
         /// Gets the profile.
         /// </summary>

+ 5 - 0
MediaBrowser.Controller/Dlna/MediaProfile.cs

@@ -19,6 +19,11 @@ namespace MediaBrowser.Controller.Dlna
         {
             Conditions = new ProfileCondition[] {};
         }
+
+        public List<string> GetContainers()
+        {
+            return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+        }
         
         public List<string> GetAudioCodecs()
         {

+ 9 - 3
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<string> GetAudioCodecs()
+        {
+            return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+        }
     }
 
     public class TranscodingSetting

+ 0 - 88
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
             }
         }
 
-        /// <summary>
-        /// Fetches from parental rating node.
-        /// </summary>
-        /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        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;
-                    }
-                }
-            }
-        }
-
         /// <summary>
         /// Gets the persons from XML node.
         /// </summary>

+ 36 - 1
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<string, string> headers)
+        {
+            return GetProfiles().FirstOrDefault(i => IsMatch(headers, i.Identification)) ??
+                GetDefaultProfile();
+        }
+
+        private bool IsMatch(IDictionary<string, string> headers, DeviceIdentification profileInfo)
+        {
+            return profileInfo.Headers.Any(i => IsMatch(headers, i));
+        }
+
+        private bool IsMatch(IDictionary<string, string> 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;
+        }
     }
 }

+ 2 - 3
MediaBrowser.Dlna/MediaBrowser.Dlna.csproj

@@ -61,7 +61,6 @@
     <Compile Include="PlayTo\DeviceService.cs" />
     <Compile Include="PlayTo\DidlBuilder.cs" />
     <Compile Include="PlayTo\DlnaController.cs" />
-    <Compile Include="PlayTo\DlnaControllerFactory.cs" />
     <Compile Include="PlayTo\Extensions.cs" />
     <Compile Include="PlayTo\PlaylistItem.cs">
       <SubType>Code</SubType>
@@ -70,7 +69,8 @@
     <Compile Include="PlayTo\PlayToManager.cs" />
     <Compile Include="PlayTo\PlayToServerEntryPoint.cs" />
     <Compile Include="PlayTo\ServiceAction.cs" />
-    <Compile Include="PlayTo\SsdpHelper.cs" />
+    <Compile Include="Profiles\Foobar2000Profile.cs" />
+    <Compile Include="Ssdp\SsdpHelper.cs" />
     <Compile Include="PlayTo\SsdpHttpClient.cs" />
     <Compile Include="PlayTo\StateVariable.cs" />
     <Compile Include="PlayTo\StreamHelper.cs" />
@@ -100,7 +100,6 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Server\DlnaServerEntryPoint.cs" />
     <Compile Include="Server\Headers.cs" />
-    <Compile Include="Server\RawHeaders.cs" />
     <Compile Include="Server\SsdpHandler.cs" />
     <Compile Include="Server\UpnpDevice.cs" />
   </ItemGroup>

+ 12 - 5
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);

+ 9 - 16
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
             };
         }
     }

+ 44 - 41
MediaBrowser.Dlna/PlayTo/DidlBuilder.cs

@@ -9,31 +9,27 @@ namespace MediaBrowser.Dlna.PlayTo
 {
     internal class DidlBuilder
     {
-        #region Constants
-
-        internal const string CRLF = "\r\n";
-        internal const string UNKNOWN = "Unknown";
-
-        internal const string DIDL_START = @"<item id=""{0}"" parentID=""{1}"" restricted=""1"" xmlns=""urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"">" + CRLF;
-        internal const string DIDL_TITLE = @"  <dc:title xmlns:dc=""http://purl.org/dc/elements/1.1/"">{0}</dc:title>" + CRLF;
-        internal const string DIDL_ARTIST = @"<upnp:artist xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:artist>" + CRLF;
-        internal const string DIDL_ALBUM = @"<upnp:album xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:album>" + CRLF;
-        internal const string DIDL_TRACKNUM = @"<upnp:originalTrackNumber xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">0</upnp:originalTrackNumber>" + CRLF;
-        internal const string DIDL_VIDEOCLASS = @"  <upnp:class xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">object.item.videoItem</upnp:class>" + CRLF;
-        internal const string DIDL_AUDIOCLASS = @"  <upnp:class xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">object.item.audioItem.musicTrack</upnp:class>" + CRLF;
-        internal const string DIDL_IMAGE = @"  <upnp:albumArtURI dlna:profileID=""JPEG_TN"" xmlns:dlna=""urn:schemas-dlna-org:metadata-1-0/"" xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:albumArtURI>" + CRLF +
-                                                @"  <upnp:icon xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:icon>" + CRLF;
-        internal const string DIDL_RELEASEDATE = @"  <dc:date xmlns:dc=""http://purl.org/dc/elements/1.1/"">{0}</dc:date>" + CRLF;
-        internal const string DIDL_GENRE = @"  <upnp:genre xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:genre>" + CRLF;
-        internal const string DESCRIPTION = @"  <dc:description xmlns:dc=""http://purl.org/dc/elements/1.1/"">{0}</dc:description>" + CRLF;
-        internal const string DIDL_VIDEO_RES = @"  <res bitrate=""{0}"" duration=""{1}"" protocolInfo=""http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000"" resolution=""{2}x{3}"" size=""0"">{4}</res>" + CRLF;
-        internal const string DIDL_AUDIO_RES = @"  <res bitrate=""{0}"" duration=""{1}"" nrAudioChannels=""2"" protocolInfo=""http-get:*:audio/mp3:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000"" sampleFrequency=""{2}"" size=""0"">{3}</res>" + CRLF;
-        internal const string DIDL_IMAGE_RES = @"  <res protocolInfo=""http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_CI=1;DLNA.ORG_FLAGS=00D00000000000000000000000000000"" resolution=""212x320"">{0}</res>" + CRLF;
-        internal const string DIDL_ALBUMIMAGE_RES = @"  <res protocolInfo=""http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_CI=1;DLNA.ORG_FLAGS=00D00000000000000000000000000000"" resolution=""320x320"">{0}</res>" + CRLF;
-        internal const string DIDL_RATING = @"  <upnp:rating xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:rating>" + CRLF;
-        internal const string DIDL_END = "</item>";
-
-        #endregion
+        const string CRLF = "\r\n";
+        const string UNKNOWN = "Unknown";
+
+        const string DIDL_START = @"<item id=""{0}"" parentID=""{1}"" restricted=""1"" xmlns=""urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"">" + CRLF;
+        const string DIDL_TITLE = @"  <dc:title xmlns:dc=""http://purl.org/dc/elements/1.1/"">{0}</dc:title>" + CRLF;
+        const string DIDL_ARTIST = @"<upnp:artist xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:artist>" + CRLF;
+        const string DIDL_ALBUM = @"<upnp:album xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:album>" + CRLF;
+        const string DIDL_TRACKNUM = @"<upnp:originalTrackNumber xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:originalTrackNumber>" + CRLF;
+        const string DIDL_VIDEOCLASS = @"  <upnp:class xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">object.item.videoItem</upnp:class>" + CRLF;
+        const string DIDL_AUDIOCLASS = @"  <upnp:class xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">object.item.audioItem.musicTrack</upnp:class>" + CRLF;
+        const string DIDL_IMAGE = @"  <upnp:albumArtURI dlna:profileID=""JPEG_TN"" xmlns:dlna=""urn:schemas-dlna-org:metadata-1-0/"" xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:albumArtURI>" + CRLF +
+                                               @"  <upnp:icon xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:icon>" + CRLF;
+        const string DIDL_RELEASEDATE = @"  <dc:date xmlns:dc=""http://purl.org/dc/elements/1.1/"">{0}</dc:date>" + CRLF;
+        const string DIDL_GENRE = @"  <upnp:genre xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:genre>" + CRLF;
+        const string DESCRIPTION = @"  <dc:description xmlns:dc=""http://purl.org/dc/elements/1.1/"">{0}</dc:description>" + CRLF;
+        const string DIDL_VIDEO_RES = @"  <res bitrate=""{0}"" duration=""{1}"" protocolInfo=""http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000"" resolution=""{2}x{3}"" size=""0"">{4}</res>" + CRLF;
+        const string DIDL_AUDIO_RES = @"  <res bitrate=""{0}"" duration=""{1}"" nrAudioChannels=""2"" protocolInfo=""http-get:*:audio/mp3:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000"" sampleFrequency=""{2}"" size=""0"">{3}</res>" + CRLF;
+        const string DIDL_IMAGE_RES = @"  <res protocolInfo=""http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_CI=1;DLNA.ORG_FLAGS=00D00000000000000000000000000000"" resolution=""212x320"">{0}</res>" + CRLF;
+        const string DIDL_ALBUMIMAGE_RES = @"  <res protocolInfo=""http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_CI=1;DLNA.ORG_FLAGS=00D00000000000000000000000000000"" resolution=""320x320"">{0}</res>" + CRLF;
+        const string DIDL_RATING = @"  <upnp:rating xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:rating>" + CRLF;
+        const string DIDL_END = "</item>";
 
         /// <summary>
         /// Builds a Didl MetaData object for the specified dto.
@@ -44,7 +40,7 @@ namespace MediaBrowser.Dlna.PlayTo
         /// <param name="streamUrl">The stream URL.</param>
         /// <param name="streams">The streams.</param>
         /// <returns>System.String.</returns>
-        internal static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable<MediaStream> streams)
+        public static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable<MediaStream> 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<MediaStream> 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<MediaStream> 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
     }
 }

+ 19 - 6
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<string> { "Audio", "Video" } }).ConfigureAwait(false);
+                await _sessionManager.OnPlaybackStart(new PlaybackInfo
+                {
+                    Item = _currentItem, 
+                    SessionId = _session.Id, 
+                    CanSeek = true, 
+                    QueueableMediaTypes = new List<string> { "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;
         }

+ 0 - 31
MediaBrowser.Dlna/PlayTo/DlnaControllerFactory.cs

@@ -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;
-        }
-    }
-}

+ 22 - 36
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<string,string> 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);
 
-                _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
-            }
-        }
+                    controller.Init(device);
 
-        /// <summary>
-        /// Gets the profile settings.
-        /// </summary>
-        /// <param name="deviceProperties">The device properties.</param>
-        /// <returns>The TranscodeSettings for the device</returns>
-        private void GetProfileSettings(DeviceInfo deviceProperties)
-        {
-            var profile = _dlnaManager.GetProfile(deviceProperties.ToDeviceIdentification());
+                    _sessionManager.ReportCapabilities(sessionInfo.Id, new SessionCapabilities
+                    {
+                        PlayableMediaTypes = new[] { MediaType.Audio, MediaType.Video, MediaType.Photo },
+                        SupportsFullscreenToggle = false
+                    });
 
-            if (!string.IsNullOrWhiteSpace(profile.Name))
-            {
-                deviceProperties.DisplayName = profile.Name;
-            }
-            if (!string.IsNullOrWhiteSpace(profile.ClientType))
-            {
-                deviceProperties.ClientType = profile.ClientType;
+                    _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
+                }
             }
         }
 

+ 0 - 2
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; }

+ 9 - 8
MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs

@@ -162,7 +162,8 @@ namespace MediaBrowser.Dlna.PlayTo
 
         private void ApplyTranscodingConditions(PlaylistItem item, IEnumerable<ProfileCondition> 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;

+ 7 - 13
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<XDocument> GetDataAsync(Uri url)
+        public async Task<XDocument> 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<HttpResponseInfo> PostSoapDataAsync(Uri url, string soapAction, string postData, string header = null, int timeOut = 20000)
+        private Task<HttpResponseInfo> 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
             };

+ 0 - 6
MediaBrowser.Dlna/PlayTo/StreamHelper.cs

@@ -43,15 +43,10 @@ namespace MediaBrowser.Dlna.PlayTo
         /// </summary>
         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<string>
             {
-                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
             };
 

+ 2 - 1
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";

+ 27 - 0
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
+                   }
+               }
+            };
+        }
+    }
+}

+ 7 - 0
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",

+ 2 - 0
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",

+ 2 - 0
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",

+ 1 - 1
MediaBrowser.Dlna/Server/Headers.cs

@@ -13,7 +13,7 @@ namespace MediaBrowser.Dlna.Server
         private readonly Dictionary<string, string> _dict = new Dictionary<string, string>();
         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;
         }

+ 0 - 16
MediaBrowser.Dlna/Server/RawHeaders.cs

@@ -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)
-        {
-        }
-    }
-}

+ 3 - 3
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());

+ 23 - 21
MediaBrowser.Dlna/PlayTo/SsdpHelper.cs → 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
         /// </summary>
         /// <param name="data">The data.</param>
         /// <returns></returns>
-        public static Uri ParseSsdpResponse(string data)
+        public static Dictionary<string,string> 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<string, string>(StringComparer.OrdinalIgnoreCase);
 
-            return !string.IsNullOrEmpty(res) ? new Uri(res.Substring(9).Trim()) : null;
-        }
-
-        /// <summary>
-        /// Parses data into SSDP event.        
-        /// </summary>
-        /// <param name="data">The data.</param>
-        /// <returns></returns>
-        [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();
+            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);
 
-            return data;
+                    if (parts.Length == 2)
+                    {
+                        headers[parts[0]] = parts[1].Trim();
+                    }
+                }
+            }
+            
+            return headers;
         }
     }
 }

+ 4 - 11
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("<IMDBrating>" + SecurityElement.Escape(video.CommunityRating.Value.ToString(UsCulture)) + "</IMDBrating>");
-            }
-
-            if (!string.IsNullOrEmpty(video.Overview))
-            {
-                builder.Append("<Description><![CDATA[" + video.Overview + "]]></Description>");
-            }
-
             var musicVideo = item as MusicVideo;
 
             if (musicVideo != null)
@@ -117,8 +106,12 @@ namespace MediaBrowser.Providers.Savers
 
             XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
                 {
+                    // Deprecated. No longer saving in this field.
                     "IMDBrating",
+                    
+                    // Deprecated. No longer saving in this field.
                     "Description",
+
                     "Artist",
                     "Album",
                     "TmdbCollectionName"

+ 4 - 6
MediaBrowser.Providers/Savers/SeriesXmlSaver.cs

@@ -60,11 +60,6 @@ namespace MediaBrowser.Providers.Savers
                 builder.Append("<id>" + SecurityElement.Escape(tvdb) + "</id>");
             }
 
-            if (!string.IsNullOrEmpty(item.Name))
-            {
-                builder.Append("<SeriesName>" + SecurityElement.Escape(item.Name) + "</SeriesName>");
-            }
-
             if (series.Status.HasValue)
             {
                 builder.Append("<Status>" + SecurityElement.Escape(series.Status.Value.ToString()) + "</Status>");
@@ -111,7 +106,6 @@ namespace MediaBrowser.Providers.Savers
             XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
                 {
                     "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"
                 });
         }

+ 22 - 6
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("<ContentRating>" + SecurityElement.Escape(item.OfficialRating) + "</ContentRating>");
-                builder.Append("<MPAARating>" + SecurityElement.Escape(item.OfficialRating) + "</MPAARating>");
-                builder.Append("<certification>" + SecurityElement.Escape(item.OfficialRating) + "</certification>");
             }
 
             builder.Append("<Added>" + SecurityElement.Escape(item.DateCreated.ToLocalTime().ToString("G")) + "</Added>");
@@ -376,16 +395,13 @@ namespace MediaBrowser.Providers.Savers
                 var timespan = TimeSpan.FromTicks(runTimeTicks.Value);
 
                 builder.Append("<RunningTime>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</RunningTime>");
-                builder.Append("<Runtime>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</Runtime>");
             }
 
             var imdb = item.GetProviderId(MetadataProviders.Imdb);
 
             if (!string.IsNullOrEmpty(imdb))
             {
-                builder.Append("<IMDB_ID>" + SecurityElement.Escape(imdb) + "</IMDB_ID>");
                 builder.Append("<IMDB>" + SecurityElement.Escape(imdb) + "</IMDB>");
-                builder.Append("<IMDbId>" + SecurityElement.Escape(imdb) + "</IMDbId>");
             }
 
             var tmdb = item.GetProviderId(MetadataProviders.Tmdb);

+ 2 - 0
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;
 

+ 1 - 1
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);