mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-03 19:17:24 -05:00 
			
		
		
		
	improved device discovery
This commit is contained in:
		
							parent
							
								
									1544b7bf9c
								
							
						
					
					
						commit
						eda8159b44
					
				@ -93,7 +93,8 @@ namespace MediaBrowser.Api.Dlna
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Headers = GetRequestHeaders(),
 | 
					                    Headers = GetRequestHeaders(),
 | 
				
			||||||
                    InputXml = await reader.ReadToEndAsync().ConfigureAwait(false),
 | 
					                    InputXml = await reader.ReadToEndAsync().ConfigureAwait(false),
 | 
				
			||||||
                    TargetServerUuId = id
 | 
					                    TargetServerUuId = id,
 | 
				
			||||||
 | 
					                    RequestedUrl = Request.AbsoluteUri
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,16 @@
 | 
				
			|||||||
using System.Globalization;
 | 
					using MediaBrowser.Common.Net;
 | 
				
			||||||
using MediaBrowser.Common.Net;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.LiveTv;
 | 
					using MediaBrowser.Controller.LiveTv;
 | 
				
			||||||
using MediaBrowser.Model.Dlna;
 | 
					using MediaBrowser.Model.Dlna;
 | 
				
			||||||
using MediaBrowser.Model.Drawing;
 | 
					using MediaBrowser.Model.Drawing;
 | 
				
			||||||
using MediaBrowser.Model.Entities;
 | 
					using MediaBrowser.Model.Entities;
 | 
				
			||||||
using MediaBrowser.Model.IO;
 | 
					using MediaBrowser.Model.IO;
 | 
				
			||||||
using MediaBrowser.Model.Logging;
 | 
					using MediaBrowser.Model.Logging;
 | 
				
			||||||
 | 
					using MediaBrowser.Model.MediaInfo;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Globalization;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using MediaBrowser.Model.MediaInfo;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MediaBrowser.Api.Playback
 | 
					namespace MediaBrowser.Api.Playback
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,8 @@ namespace MediaBrowser.Controller.Dlna
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public string TargetServerUuId { get; set; }
 | 
					        public string TargetServerUuId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string RequestedUrl { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ControlRequest()
 | 
					        public ControlRequest()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Headers = new Dictionary<string, string>();
 | 
					            Headers = new Dictionary<string, string>();
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,7 @@ namespace MediaBrowser.Dlna.Didl
 | 
				
			|||||||
    public class DidlBuilder
 | 
					    public class DidlBuilder
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 | 
					        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
 | 
					        private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
 | 
				
			||||||
        private const string NS_DC = "http://purl.org/dc/elements/1.1/";
 | 
					        private const string NS_DC = "http://purl.org/dc/elements/1.1/";
 | 
				
			||||||
        private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
 | 
					        private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
 | 
				
			||||||
@ -298,7 +298,7 @@ namespace MediaBrowser.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            container.AppendChild(res);
 | 
					            container.AppendChild(res);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        public XmlElement GetFolderElement(XmlDocument doc, Folder folder, int childCount, Filter filter)
 | 
					        public XmlElement GetFolderElement(XmlDocument doc, Folder folder, int childCount, Filter filter)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var container = doc.CreateElement(string.Empty, "container", NS_DIDL);
 | 
					            var container = doc.CreateElement(string.Empty, "container", NS_DIDL);
 | 
				
			||||||
@ -450,9 +450,14 @@ namespace MediaBrowser.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void AddPeople(BaseItem item, XmlElement element)
 | 
					        private void AddPeople(BaseItem item, XmlElement element)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var types = new[] { PersonType.Director, PersonType.Writer, PersonType.Producer, PersonType.Composer, "Creator" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var actor in item.People)
 | 
					            foreach (var actor in item.People)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                AddValue(element, "upnp", (actor.Type ?? PersonType.Actor).ToLower(), actor.Name, NS_UPNP);
 | 
					                var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
					                    ?? PersonType.Actor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                AddValue(element, "upnp", type.ToLower(), actor.Name, NS_UPNP);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -25,10 +25,10 @@ namespace MediaBrowser.Dlna
 | 
				
			|||||||
        private readonly ILogger _logger;
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
        private readonly IJsonSerializer _jsonSerializer;
 | 
					        private readonly IJsonSerializer _jsonSerializer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DlnaManager(IXmlSerializer xmlSerializer, 
 | 
					        public DlnaManager(IXmlSerializer xmlSerializer,
 | 
				
			||||||
            IFileSystem fileSystem, 
 | 
					            IFileSystem fileSystem,
 | 
				
			||||||
            IApplicationPaths appPaths, 
 | 
					            IApplicationPaths appPaths,
 | 
				
			||||||
            ILogger logger, 
 | 
					            ILogger logger,
 | 
				
			||||||
            IJsonSerializer jsonSerializer)
 | 
					            IJsonSerializer jsonSerializer)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _xmlSerializer = xmlSerializer;
 | 
					            _xmlSerializer = xmlSerializer;
 | 
				
			||||||
@ -230,6 +230,19 @@ namespace MediaBrowser.Dlna
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                _logger.Debug("Found matching device profile: {0}", profile.Name);
 | 
					                _logger.Debug("Found matching device profile: {0}", profile.Name);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                string userAgent = null;
 | 
				
			||||||
 | 
					                headers.TryGetValue("User-Agent", out userAgent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var msg = "No matching device profile found. The default will be used. ";
 | 
				
			||||||
 | 
					                if (!string.IsNullOrEmpty(userAgent))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    msg += "User-agent: " + userAgent + ". ";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _logger.Debug(msg);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return profile;
 | 
					            return profile;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -3,81 +3,102 @@ using MediaBrowser.Common.Extensions;
 | 
				
			|||||||
using MediaBrowser.Common.Net;
 | 
					using MediaBrowser.Common.Net;
 | 
				
			||||||
using MediaBrowser.Controller.Configuration;
 | 
					using MediaBrowser.Controller.Configuration;
 | 
				
			||||||
using MediaBrowser.Controller.Plugins;
 | 
					using MediaBrowser.Controller.Plugins;
 | 
				
			||||||
 | 
					using MediaBrowser.Dlna.Ssdp;
 | 
				
			||||||
using MediaBrowser.Model.Logging;
 | 
					using MediaBrowser.Model.Logging;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Net;
 | 
					using System.Net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MediaBrowser.Dlna.Server
 | 
					namespace MediaBrowser.Dlna.Main
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class DlnaServerEntryPoint : IServerEntryPoint
 | 
					    public class DlnaEntryPoint : IServerEntryPoint
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly IServerConfigurationManager _config;
 | 
					        private readonly IServerConfigurationManager _config;
 | 
				
			||||||
        private readonly ILogger _logger;
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private SsdpHandler _ssdpHandler;
 | 
					 | 
				
			||||||
        private readonly IApplicationHost _appHost;
 | 
					        private readonly IApplicationHost _appHost;
 | 
				
			||||||
        private readonly INetworkManager _network;
 | 
					        private readonly INetworkManager _network;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static DlnaServerEntryPoint Instance;
 | 
					        private SsdpHandler _ssdpHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DlnaServerEntryPoint(IServerConfigurationManager config, ILogManager logManager, IApplicationHost appHost, INetworkManager network)
 | 
					        private readonly List<Guid> _registeredServerIds = new List<Guid>();
 | 
				
			||||||
 | 
					        private bool _dlnaServerStarted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public DlnaEntryPoint(IServerConfigurationManager config, ILogManager logManager, IApplicationHost appHost, INetworkManager network)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Instance = this;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _config = config;
 | 
					            _config = config;
 | 
				
			||||||
            _appHost = appHost;
 | 
					            _appHost = appHost;
 | 
				
			||||||
            _network = network;
 | 
					            _network = network;
 | 
				
			||||||
            _logger = logManager.GetLogger("DlnaServer");
 | 
					            _logger = logManager.GetLogger("Dlna");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void Run()
 | 
					        public void Run()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _config.ConfigurationUpdated += ConfigurationUpdated;
 | 
					            StartSsdpHandler();
 | 
				
			||||||
 | 
					            ReloadComponents();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ReloadServer();
 | 
					            _config.ConfigurationUpdated += ConfigurationUpdated;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void ConfigurationUpdated(object sender, EventArgs e)
 | 
					        void ConfigurationUpdated(object sender, EventArgs e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ReloadServer();
 | 
					            ReloadComponents();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void ReloadServer()
 | 
					        private void ReloadComponents()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var isStarted = _ssdpHandler != null;
 | 
					            var isStarted = _dlnaServerStarted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (_config.Configuration.DlnaOptions.EnableServer && !isStarted)
 | 
					            if (_config.Configuration.DlnaOptions.EnableServer && !isStarted)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                StartServer();
 | 
					                StartDlnaServer();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else if (!_config.Configuration.DlnaOptions.EnableServer && isStarted)
 | 
					            else if (!_config.Configuration.DlnaOptions.EnableServer && isStarted)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                DisposeServer();
 | 
					                DisposeDlnaServer();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly object _syncLock = new object();
 | 
					        private void StartSsdpHandler()
 | 
				
			||||||
        private void StartServer()
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var signature = GenerateServerSignature();
 | 
					            try
 | 
				
			||||||
 | 
					 | 
				
			||||||
            lock (_syncLock)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                try
 | 
					                _ssdpHandler = new SsdpHandler(_logger, _config, GenerateServerSignature());
 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _ssdpHandler = new SsdpHandler(_logger, _config, signature);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    RegisterEndpoints();
 | 
					                _ssdpHandler.Start();
 | 
				
			||||||
                }
 | 
					            }
 | 
				
			||||||
                catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
                {
 | 
					            {
 | 
				
			||||||
                    _logger.ErrorException("Error starting Dlna server", ex);
 | 
					                _logger.ErrorException("Error starting Dlna server", ex);
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void RegisterEndpoints()
 | 
					        private void DisposeSsdpHandler()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _ssdpHandler.Dispose();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception ex)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _logger.ErrorException("Error disposing ssdp handler", ex);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void StartDlnaServer()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                RegisterServerEndpoints();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _dlnaServerStarted = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception ex)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _logger.ErrorException("Error registering endpoint", ex);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void RegisterServerEndpoints()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var address in _network.GetLocalIpAddresses())
 | 
					            foreach (var address in _network.GetLocalIpAddresses())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -87,31 +108,17 @@ namespace MediaBrowser.Dlna.Server
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                var uri = new Uri(string.Format("http://{0}:{1}{2}", address, _config.Configuration.HttpServerPortNumber, descriptorURI));
 | 
					                var uri = new Uri(string.Format("http://{0}:{1}{2}", address, _config.Configuration.HttpServerPortNumber, descriptorURI));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                _ssdpHandler.RegisterNotification(guid, uri, IPAddress.Parse(address));
 | 
					                var services = new List<string>
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public UpnpDevice GetServerUpnpDevice(string uuid)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return _ssdpHandler.Devices.FirstOrDefault(i => string.Equals(uuid, i.Uuid.ToString("N"), StringComparison.OrdinalIgnoreCase));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void DisposeServer()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            lock (_syncLock)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (_ssdpHandler != null)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    try
 | 
					                    "upnp:rootdevice", 
 | 
				
			||||||
                    {
 | 
					                    "urn:schemas-upnp-org:device:MediaServer:1", 
 | 
				
			||||||
                        _ssdpHandler.Dispose();
 | 
					                    "urn:schemas-upnp-org:service:ContentDirectory:1", 
 | 
				
			||||||
                    }
 | 
					                    "uuid:" + guid.ToString("N")
 | 
				
			||||||
                    catch (Exception ex)
 | 
					                };
 | 
				
			||||||
                    {
 | 
					
 | 
				
			||||||
                        _logger.ErrorException("Error disposing Dlna server", ex);
 | 
					                _ssdpHandler.RegisterNotification(guid, uri, IPAddress.Parse(address), services);
 | 
				
			||||||
                    }
 | 
					
 | 
				
			||||||
                    _ssdpHandler = null;
 | 
					                _registeredServerIds.Add(guid);
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -140,7 +147,27 @@ namespace MediaBrowser.Dlna.Server
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public void Dispose()
 | 
					        public void Dispose()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            DisposeServer();
 | 
					            DisposeDlnaServer();
 | 
				
			||||||
 | 
					            DisposeSsdpHandler();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void DisposeDlnaServer()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            foreach (var id in _registeredServerIds)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                try
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _ssdpHandler.UnregisterNotification(id);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (Exception ex)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _logger.ErrorException("Error unregistering server", ex);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _registeredServerIds.Clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _dlnaServerStarted = false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -54,6 +54,7 @@
 | 
				
			|||||||
    <Compile Include="DlnaManager.cs" />
 | 
					    <Compile Include="DlnaManager.cs" />
 | 
				
			||||||
    <Compile Include="Common\Argument.cs" />
 | 
					    <Compile Include="Common\Argument.cs" />
 | 
				
			||||||
    <Compile Include="Eventing\EventManager.cs" />
 | 
					    <Compile Include="Eventing\EventManager.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Main\DlnaEntryPoint.cs" />
 | 
				
			||||||
    <Compile Include="PlayTo\CurrentIdEventArgs.cs" />
 | 
					    <Compile Include="PlayTo\CurrentIdEventArgs.cs" />
 | 
				
			||||||
    <Compile Include="PlayTo\Device.cs">
 | 
					    <Compile Include="PlayTo\Device.cs">
 | 
				
			||||||
      <SubType>Code</SubType>
 | 
					      <SubType>Code</SubType>
 | 
				
			||||||
@ -79,7 +80,7 @@
 | 
				
			|||||||
    <Compile Include="Server\ControlHandler.cs" />
 | 
					    <Compile Include="Server\ControlHandler.cs" />
 | 
				
			||||||
    <Compile Include="Server\ServiceActionListBuilder.cs" />
 | 
					    <Compile Include="Server\ServiceActionListBuilder.cs" />
 | 
				
			||||||
    <Compile Include="Server\ContentDirectoryXmlBuilder.cs" />
 | 
					    <Compile Include="Server\ContentDirectoryXmlBuilder.cs" />
 | 
				
			||||||
    <Compile Include="Server\Datagram.cs" />
 | 
					    <Compile Include="Ssdp\Datagram.cs" />
 | 
				
			||||||
    <Compile Include="Server\DescriptionXmlBuilder.cs" />
 | 
					    <Compile Include="Server\DescriptionXmlBuilder.cs" />
 | 
				
			||||||
    <Compile Include="Ssdp\SsdpHelper.cs" />
 | 
					    <Compile Include="Ssdp\SsdpHelper.cs" />
 | 
				
			||||||
    <Compile Include="PlayTo\SsdpHttpClient.cs" />
 | 
					    <Compile Include="PlayTo\SsdpHttpClient.cs" />
 | 
				
			||||||
@ -108,10 +109,11 @@
 | 
				
			|||||||
    <Compile Include="Profiles\Xbox360Profile.cs" />
 | 
					    <Compile Include="Profiles\Xbox360Profile.cs" />
 | 
				
			||||||
    <Compile Include="Profiles\XboxOneProfile.cs" />
 | 
					    <Compile Include="Profiles\XboxOneProfile.cs" />
 | 
				
			||||||
    <Compile Include="Properties\AssemblyInfo.cs" />
 | 
					    <Compile Include="Properties\AssemblyInfo.cs" />
 | 
				
			||||||
    <Compile Include="Server\DlnaServerEntryPoint.cs" />
 | 
					 | 
				
			||||||
    <Compile Include="Server\Headers.cs" />
 | 
					    <Compile Include="Server\Headers.cs" />
 | 
				
			||||||
    <Compile Include="Server\SsdpHandler.cs" />
 | 
					 | 
				
			||||||
    <Compile Include="Server\UpnpDevice.cs" />
 | 
					    <Compile Include="Server\UpnpDevice.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Ssdp\SsdpMessageBuilder.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Ssdp\SsdpMessageEventArgs.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Ssdp\SsdpHandler.cs" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
 | 
					    <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
 | 
				
			||||||
 | 
				
			|||||||
@ -50,7 +50,7 @@ namespace MediaBrowser.Dlna.PlayTo
 | 
				
			|||||||
                if (_device == null || _device.UpdateTime == default(DateTime))
 | 
					                if (_device == null || _device.UpdateTime == default(DateTime))
 | 
				
			||||||
                    return false;
 | 
					                    return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return DateTime.UtcNow <= _device.UpdateTime.AddSeconds(30);
 | 
					                return DateTime.UtcNow <= _device.UpdateTime.AddMinutes(10);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
using MediaBrowser.Common.Net;
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using MediaBrowser.Common.Net;
 | 
				
			||||||
using MediaBrowser.Controller;
 | 
					using MediaBrowser.Controller;
 | 
				
			||||||
using MediaBrowser.Controller.Configuration;
 | 
					using MediaBrowser.Controller.Configuration;
 | 
				
			||||||
using MediaBrowser.Controller.Dlna;
 | 
					using MediaBrowser.Controller.Dlna;
 | 
				
			||||||
@ -68,7 +69,7 @@ namespace MediaBrowser.Dlna.PlayTo
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                _logger.Debug("Found interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
 | 
					                _logger.Debug("Found interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (!network.SupportsMulticast || !network.GetIPProperties().MulticastAddresses.Any())
 | 
					                if (!network.SupportsMulticast || OperationalStatus.Up != network.OperationalStatus || !network.GetIPProperties().MulticastAddresses.Any())
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var ipV4 = network.GetIPProperties().GetIPv4Properties();
 | 
					                var ipV4 = network.GetIPProperties().GetIPv4Properties();
 | 
				
			||||||
@ -84,7 +85,7 @@ namespace MediaBrowser.Dlna.PlayTo
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    try
 | 
					                    try
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        CreateListener(localIp);
 | 
					                        CreateListener(localIp, ipV4.Index);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch (Exception e)
 | 
					                    catch (Exception e)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@ -111,15 +112,15 @@ namespace MediaBrowser.Dlna.PlayTo
 | 
				
			|||||||
        /// Creates a socket for the interface and listends for data.
 | 
					        /// Creates a socket for the interface and listends for data.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <param name="localIp">The local ip.</param>
 | 
					        /// <param name="localIp">The local ip.</param>
 | 
				
			||||||
        private void CreateListener(IPAddress localIp)
 | 
					        private void CreateListener(IPAddress localIp, int networkInterfaceIndex)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Task.Factory.StartNew(async (o) =>
 | 
					            Task.Factory.StartNew(async (o) =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var socket = GetMulticastSocket();
 | 
					                    var socket = GetMulticastSocket(networkInterfaceIndex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    socket.Bind(new IPEndPoint(localIp, 0));
 | 
					                    socket.Bind(new IPEndPoint(localIp, 1900));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    _logger.Info("Creating SSDP listener");
 | 
					                    _logger.Info("Creating SSDP listener");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -183,7 +184,8 @@ namespace MediaBrowser.Dlna.PlayTo
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var request = SsdpHelper.CreateRendererSSDP(3);
 | 
					                    var msg = new SsdpMessageBuilder().BuildRendererDiscoveryMessage();
 | 
				
			||||||
 | 
					                    var request = Encoding.UTF8.GetBytes(msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    while (true)
 | 
					                    while (true)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@ -210,12 +212,12 @@ namespace MediaBrowser.Dlna.PlayTo
 | 
				
			|||||||
        /// Gets a socket configured for SDDP multicasting.
 | 
					        /// Gets a socket configured for SDDP multicasting.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <returns></returns>
 | 
					        /// <returns></returns>
 | 
				
			||||||
        private Socket GetMulticastSocket()
 | 
					        private Socket GetMulticastSocket(int networkInterfaceIndex)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
 | 
					            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
 | 
				
			||||||
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
 | 
					            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
 | 
				
			||||||
            socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250")));
 | 
					            socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), networkInterfaceIndex));
 | 
				
			||||||
            //socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 3);
 | 
					            socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
 | 
				
			||||||
            return socket;
 | 
					            return socket;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
using System.Xml.Serialization;
 | 
					using MediaBrowser.Model.Dlna;
 | 
				
			||||||
using MediaBrowser.Controller.Dlna;
 | 
					using System.Xml.Serialization;
 | 
				
			||||||
using MediaBrowser.Model.Dlna;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MediaBrowser.Dlna.Profiles
 | 
					namespace MediaBrowser.Dlna.Profiles
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,17 @@ namespace MediaBrowser.Dlna.Profiles
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            Identification = new DeviceIdentification
 | 
					            Identification = new DeviceIdentification
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                ModelUrl = "samsung.com"
 | 
					                ModelUrl = "samsung.com",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Headers = new[]
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    new HttpHeaderInfo
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Name = "User-Agent",
 | 
				
			||||||
 | 
					                        Value = @"SEC_",
 | 
				
			||||||
 | 
					                        Match = HeaderMatchType.Substring
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            XmlRootAttributes = new[]
 | 
					            XmlRootAttributes = new[]
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
using System.Xml.Serialization;
 | 
					using MediaBrowser.Model.Dlna;
 | 
				
			||||||
using MediaBrowser.Controller.Dlna;
 | 
					using System.Xml.Serialization;
 | 
				
			||||||
using MediaBrowser.Model.Dlna;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MediaBrowser.Dlna.Profiles
 | 
					namespace MediaBrowser.Dlna.Profiles
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
using System.Xml.Serialization;
 | 
					using MediaBrowser.Model.Dlna;
 | 
				
			||||||
using MediaBrowser.Controller.Dlna;
 | 
					using System.Xml.Serialization;
 | 
				
			||||||
using MediaBrowser.Model.Dlna;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MediaBrowser.Dlna.Profiles
 | 
					namespace MediaBrowser.Dlna.Profiles
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,9 @@
 | 
				
			|||||||
  <Name>Samsung Smart TV</Name>
 | 
					  <Name>Samsung Smart TV</Name>
 | 
				
			||||||
  <Identification>
 | 
					  <Identification>
 | 
				
			||||||
    <ModelUrl>samsung.com</ModelUrl>
 | 
					    <ModelUrl>samsung.com</ModelUrl>
 | 
				
			||||||
    <Headers />
 | 
					    <Headers>
 | 
				
			||||||
 | 
					      <HttpHeaderInfo name="User-Agent" value="SEC_" match="Substring" />
 | 
				
			||||||
 | 
					    </Headers>
 | 
				
			||||||
  </Identification>
 | 
					  </Identification>
 | 
				
			||||||
  <FriendlyName>Media Browser</FriendlyName>
 | 
					  <FriendlyName>Media Browser</FriendlyName>
 | 
				
			||||||
  <Manufacturer>Media Browser</Manufacturer>
 | 
					  <Manufacturer>Media Browser</Manufacturer>
 | 
				
			||||||
 | 
				
			|||||||
@ -67,10 +67,8 @@ namespace MediaBrowser.Dlna.Server
 | 
				
			|||||||
            var profile = _dlna.GetProfile(request.Headers) ??
 | 
					            var profile = _dlna.GetProfile(request.Headers) ??
 | 
				
			||||||
                          _dlna.GetDefaultProfile();
 | 
					                          _dlna.GetDefaultProfile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var device = DlnaServerEntryPoint.Instance.GetServerUpnpDevice(request.TargetServerUuId);
 | 
					            var serverAddress = request.RequestedUrl.Substring(0, request.RequestedUrl.IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            var serverAddress = device.Descriptor.ToString().Substring(0, device.Descriptor.ToString().IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var user = GetUser(profile);
 | 
					            var user = GetUser(profile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new ControlHandler(
 | 
					            return new ControlHandler(
 | 
				
			||||||
 | 
				
			|||||||
@ -4,24 +4,31 @@ using System.Net;
 | 
				
			|||||||
using System.Net.Sockets;
 | 
					using System.Net.Sockets;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MediaBrowser.Dlna.Server
 | 
					namespace MediaBrowser.Dlna.Ssdp
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class Datagram
 | 
					    public class Datagram
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public IPEndPoint EndPoint { get; private set; }
 | 
					        public IPEndPoint EndPoint { get; private set; }
 | 
				
			||||||
        public IPAddress LocalAddress { get; private set; }
 | 
					        public IPAddress LocalAddress { get; private set; }
 | 
				
			||||||
        public string Message { get; private set; }
 | 
					        public string Message { get; private set; }
 | 
				
			||||||
        public bool Sticky { get; private set; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// The number of times to send the message
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public int TotalSendCount { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// The number of times the message has been sent
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
        public int SendCount { get; private set; }
 | 
					        public int SendCount { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly ILogger _logger;
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Datagram(IPEndPoint endPoint, IPAddress localAddress, ILogger logger, string message, bool sticky)
 | 
					        public Datagram(IPEndPoint endPoint, IPAddress localAddress, ILogger logger, string message, int totalSendCount)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Message = message;
 | 
					            Message = message;
 | 
				
			||||||
            _logger = logger;
 | 
					            _logger = logger;
 | 
				
			||||||
            Sticky = sticky;
 | 
					            TotalSendCount = totalSendCount;
 | 
				
			||||||
            LocalAddress = localAddress;
 | 
					            LocalAddress = localAddress;
 | 
				
			||||||
            EndPoint = endPoint;
 | 
					            EndPoint = endPoint;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -31,9 +38,11 @@ namespace MediaBrowser.Dlna.Server
 | 
				
			|||||||
            var msg = Encoding.ASCII.GetBytes(Message);
 | 
					            var msg = Encoding.ASCII.GetBytes(Message);
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var client = new UdpClient();
 | 
					                var client = CreateSocket();
 | 
				
			||||||
                client.Client.Bind(new IPEndPoint(LocalAddress, 0));
 | 
					
 | 
				
			||||||
                client.BeginSend(msg, msg.Length, EndPoint, result =>
 | 
					                client.Bind(new IPEndPoint(LocalAddress, 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                client.BeginSendTo(msg, 0, msg.Length, SocketFlags.None, EndPoint, result =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    try
 | 
					                    try
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@ -61,5 +70,13 @@ namespace MediaBrowser.Dlna.Server
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            ++SendCount;
 | 
					            ++SendCount;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private Socket CreateSocket()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
 | 
				
			||||||
 | 
					            return socket;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
using MediaBrowser.Controller.Configuration;
 | 
					using MediaBrowser.Controller.Configuration;
 | 
				
			||||||
 | 
					using MediaBrowser.Dlna.Server;
 | 
				
			||||||
using MediaBrowser.Model.Logging;
 | 
					using MediaBrowser.Model.Logging;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
@ -10,190 +11,153 @@ using System.Net.Sockets;
 | 
				
			|||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MediaBrowser.Dlna.Server
 | 
					namespace MediaBrowser.Dlna.Ssdp
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class SsdpHandler : IDisposable
 | 
					    public class SsdpHandler : IDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly AutoResetEvent _datagramPosted = new AutoResetEvent(false);
 | 
					        private Socket _socket;
 | 
				
			||||||
        private readonly ConcurrentQueue<Datagram> _messageQueue = new ConcurrentQueue<Datagram>();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly ILogger _logger;
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
        private readonly IServerConfigurationManager _config;
 | 
					        private readonly IServerConfigurationManager _config;
 | 
				
			||||||
        private readonly string _serverSignature;
 | 
					 | 
				
			||||||
        private bool _isDisposed;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const string SSDPAddr = "239.255.255.250";
 | 
					        const string SSDPAddr = "239.255.255.250";
 | 
				
			||||||
        const int SSDPPort = 1900;
 | 
					        const int SSDPPort = 1900;
 | 
				
			||||||
 | 
					        private readonly string _serverSignature;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort);
 | 
					 | 
				
			||||||
        private readonly IPAddress _ssdpIp = IPAddress.Parse(SSDPAddr);
 | 
					        private readonly IPAddress _ssdpIp = IPAddress.Parse(SSDPAddr);
 | 
				
			||||||
 | 
					        private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort);
 | 
				
			||||||
        private UdpClient _udpClient;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private readonly Dictionary<Guid, List<UpnpDevice>> _devices = new Dictionary<Guid, List<UpnpDevice>>();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private Timer _queueTimer;
 | 
					        private Timer _queueTimer;
 | 
				
			||||||
        private Timer _notificationTimer;
 | 
					        private Timer _notificationTimer;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        private readonly AutoResetEvent _datagramPosted = new AutoResetEvent(false);
 | 
				
			||||||
 | 
					        private readonly ConcurrentQueue<Datagram> _messageQueue = new ConcurrentQueue<Datagram>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private bool _isDisposed;
 | 
				
			||||||
 | 
					        private readonly ConcurrentDictionary<Guid, List<UpnpDevice>> _devices = new ConcurrentDictionary<Guid, List<UpnpDevice>>();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
        public SsdpHandler(ILogger logger, IServerConfigurationManager config, string serverSignature)
 | 
					        public SsdpHandler(ILogger logger, IServerConfigurationManager config, string serverSignature)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _logger = logger;
 | 
					            _logger = logger;
 | 
				
			||||||
            _config = config;
 | 
					            _config = config;
 | 
				
			||||||
            _serverSignature = serverSignature;
 | 
					            _serverSignature = serverSignature;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            Start();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IEnumerable<UpnpDevice> Devices
 | 
					        public event EventHandler<SsdpMessageEventArgs> MessageReceived;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnMessageReceived(SsdpMessageEventArgs args)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get
 | 
					            if (string.Equals(args.Method, "M-SEARCH", StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                UpnpDevice[] devs;
 | 
					                RespondToSearch(args.EndPoint, args.Headers["st"]);
 | 
				
			||||||
                lock (_devices)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    devs = _devices.Values.SelectMany(i => i).ToArray();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return devs;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void Start()
 | 
					        public IEnumerable<UpnpDevice> RegisteredDevices
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _udpClient = new UdpClient();
 | 
					            get
 | 
				
			||||||
            _udpClient.Client.UseOnlyOverlappedIO = true;
 | 
					            {
 | 
				
			||||||
            _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
 | 
					                return _devices.Values.SelectMany(i => i).ToList();
 | 
				
			||||||
            _udpClient.ExclusiveAddressUse = false;
 | 
					            }
 | 
				
			||||||
            _udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, SSDPPort));
 | 
					        }
 | 
				
			||||||
            _udpClient.JoinMulticastGroup(_ssdpIp, 2);
 | 
					        
 | 
				
			||||||
 | 
					        public void Start()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _socket = CreateMulticastSocket();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _logger.Info("SSDP service started");
 | 
					            _logger.Info("SSDP service started");
 | 
				
			||||||
            Receive();
 | 
					            Receive();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            StartNotificationTimer();
 | 
					            StartNotificationTimer();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void Receive()
 | 
					        public void SendDatagram(string header,
 | 
				
			||||||
 | 
					            Dictionary<string, string> values,
 | 
				
			||||||
 | 
					            IPAddress localAddress,
 | 
				
			||||||
 | 
					            int sendCount = 1)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            SendDatagram(header, values, _ssdpEndp, localAddress, sendCount);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void SendDatagram(string header, 
 | 
				
			||||||
 | 
					            Dictionary<string, string> values, 
 | 
				
			||||||
 | 
					            IPEndPoint endpoint,
 | 
				
			||||||
 | 
					            IPAddress localAddress,
 | 
				
			||||||
 | 
					            int sendCount = 1)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var msg = new SsdpMessageBuilder().BuildMessage(header, values);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var dgram = new Datagram(endpoint, localAddress, _logger, msg, sendCount);
 | 
				
			||||||
 | 
					            if (_messageQueue.Count == 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _udpClient.BeginReceive(ReceiveCallback, null);
 | 
					                dgram.Send();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (ObjectDisposedException)
 | 
					
 | 
				
			||||||
 | 
					            _messageQueue.Enqueue(dgram);
 | 
				
			||||||
 | 
					            StartQueueTimer();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void SendDatagramFromDevices(string header,
 | 
				
			||||||
 | 
					            Dictionary<string, string> values,
 | 
				
			||||||
 | 
					            IPEndPoint endpoint,
 | 
				
			||||||
 | 
					            string deviceType)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            foreach (var d in RegisteredDevices)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                if (string.Equals(deviceType, "ssdp:all", StringComparison.OrdinalIgnoreCase) || 
 | 
				
			||||||
 | 
					                    string.Equals(deviceType, d.Type, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    SendDatagram(header, values, endpoint, d.Address);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void ReceiveCallback(IAsyncResult result)
 | 
					        private void RespondToSearch(IPEndPoint endpoint, string deviceType)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var endpoint = new IPEndPoint(IPAddress.None, SSDPPort);
 | 
					 | 
				
			||||||
                var received = _udpClient.EndReceive(result, ref endpoint);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (_config.Configuration.DlnaOptions.EnableDebugLogging)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _logger.Debug("{0} - SSDP Received a datagram", endpoint);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                using (var reader = new StreamReader(new MemoryStream(received), Encoding.ASCII))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var proto = (reader.ReadLine() ?? string.Empty).Trim();
 | 
					 | 
				
			||||||
                    var method = proto.Split(new[] { ' ' }, 2)[0];
 | 
					 | 
				
			||||||
                    var headers = new Headers();
 | 
					 | 
				
			||||||
                    for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        line = line.Trim();
 | 
					 | 
				
			||||||
                        if (string.IsNullOrEmpty(line))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            break;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        var parts = line.Split(new[] { ':' }, 2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (parts.Length >= 2)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            headers[parts[0]] = parts[1].Trim();
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (_config.Configuration.DlnaOptions.EnableDebugLogging)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        _logger.Debug("{0} - Datagram method: {1}", endpoint, method);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (string.Equals(method, "M-SEARCH", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        RespondToSearch(endpoint, headers["st"]);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception ex)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _logger.ErrorException("Failed to read SSDP message", ex);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!_isDisposed)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Receive();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void RespondToSearch(IPEndPoint endpoint, string req)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (string.Equals(req, "ssdp:all", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                req = null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
 | 
					            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _logger.Debug("RespondToSearch");
 | 
					                _logger.Debug("RespondToSearch");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var d in Devices)
 | 
					            const string header = "HTTP/1.1 200 OK";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var d in RegisteredDevices)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!string.IsNullOrEmpty(req) && !string.Equals(req, d.Type, StringComparison.OrdinalIgnoreCase))
 | 
					                if (string.Equals(deviceType, "ssdp:all", StringComparison.OrdinalIgnoreCase) ||
 | 
				
			||||||
 | 
					                    string.Equals(deviceType, d.Type, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    continue;
 | 
					                    var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    values["CACHE-CONTROL"] = "max-age = 600";
 | 
				
			||||||
 | 
					                    values["DATE"] = DateTime.Now.ToString("R");
 | 
				
			||||||
 | 
					                    values["EXT"] = "";
 | 
				
			||||||
 | 
					                    values["LOCATION"] = d.Descriptor.ToString();
 | 
				
			||||||
 | 
					                    values["SERVER"] = _serverSignature;
 | 
				
			||||||
 | 
					                    values["ST"] = d.Type;
 | 
				
			||||||
 | 
					                    values["USN"] = d.USN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    SendDatagram(header, values, endpoint, d.Address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    _logger.Info("{1} - Responded to a {0} request to {2}", d.Type, endpoint, d.Address.ToString());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            } 
 | 
				
			||||||
                SendSearchResponse(endpoint, d);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void SendSearchResponse(IPEndPoint endpoint, UpnpDevice dev)
 | 
					        private readonly object _queueTimerSyncLock = new object();
 | 
				
			||||||
 | 
					        private void StartQueueTimer()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var builder = new StringBuilder();
 | 
					            lock (_queueTimerSyncLock)
 | 
				
			||||||
 | 
					 | 
				
			||||||
            const string argFormat = "{0}: {1}\r\n";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            builder.Append("HTTP/1.1 200 OK\r\n");
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "CACHE-CONTROL", "max-age = 600");
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "DATE", DateTime.Now.ToString("R"));
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "EXT", "");
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "LOCATION", dev.Descriptor);
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "SERVER", _serverSignature);
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "ST", dev.Type);
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "USN", dev.USN);
 | 
					 | 
				
			||||||
            builder.Append("\r\n");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            SendDatagram(endpoint, dev.Address, builder.ToString(), false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _logger.Info("{1} - Responded to a {0} request to {2}", dev.Type, endpoint, dev.Address.ToString());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void SendDatagram(IPEndPoint endpoint, IPAddress localAddress, string msg, bool sticky)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_isDisposed)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return;
 | 
					                if (_queueTimer == null)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _queueTimer = new Timer(QueueTimerCallback, null, 1000, Timeout.Infinite);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _queueTimer.Change(1000, Timeout.Infinite);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            var dgram = new Datagram(endpoint, localAddress, _logger, msg, sticky);
 | 
					 | 
				
			||||||
            if (_messageQueue.Count == 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                dgram.Send();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            _messageQueue.Enqueue(dgram);
 | 
					 | 
				
			||||||
            StartQueueTimer();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void QueueTimerCallback(object state)
 | 
					        private void QueueTimerCallback(object state)
 | 
				
			||||||
@ -206,10 +170,10 @@ namespace MediaBrowser.Dlna.Server
 | 
				
			|||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (msg != null && (!_isDisposed || msg.Sticky))
 | 
					                if (msg != null && (!_isDisposed || msg.TotalSendCount > 1))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    msg.Send();
 | 
					                    msg.Send();
 | 
				
			||||||
                    if (msg.SendCount > 2)
 | 
					                    if (msg.SendCount > msg.TotalSendCount)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        _messageQueue.TryDequeue(out msg);
 | 
					                        _messageQueue.TryDequeue(out msg);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -231,84 +195,81 @@ namespace MediaBrowser.Dlna.Server
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void NotifyAll()
 | 
					        private void Receive()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _logger.Debug("Sending alive notifications");
 | 
					                var buffer = new byte[1024];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                EndPoint endpoint = new IPEndPoint(IPAddress.Any, SSDPPort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _socket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref endpoint, ReceiveCallback, buffer);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            foreach (var d in Devices)
 | 
					            catch (ObjectDisposedException)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                NotifyDevice(d, "alive", false);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void NotifyDevice(UpnpDevice dev, string type, bool sticky)
 | 
					        private void ReceiveCallback(IAsyncResult result)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var builder = new StringBuilder();
 | 
					            if (_isDisposed)
 | 
				
			||||||
 | 
					 | 
				
			||||||
            const string argFormat = "{0}: {1}\r\n";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            builder.Append("NOTIFY * HTTP/1.1\r\n{0}\r\n");
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "HOST", "239.255.255.250:1900");
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "CACHE-CONTROL", "max-age = 600");
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "LOCATION", dev.Descriptor);
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "SERVER", _serverSignature);
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "NTS", "ssdp:" + type);
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "NT", dev.Type);
 | 
					 | 
				
			||||||
            builder.AppendFormat(argFormat, "USN", dev.USN);
 | 
					 | 
				
			||||||
            builder.Append("\r\n");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _logger.Debug("{0} said {1}", dev.USN, type);
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            SendDatagram(_ssdpEndp, dev.Address, builder.ToString(), sticky);
 | 
					            try
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void RegisterNotification(Guid uuid, Uri descriptor, IPAddress address)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            List<UpnpDevice> list;
 | 
					 | 
				
			||||||
            lock (_devices)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!_devices.TryGetValue(uuid, out list))
 | 
					                EndPoint endpoint = new IPEndPoint(IPAddress.Any, SSDPPort);
 | 
				
			||||||
 | 
					                var receivedCount = _socket.EndReceiveFrom(result, ref endpoint);
 | 
				
			||||||
 | 
					                var received = (byte[])result.AsyncState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (_config.Configuration.DlnaOptions.EnableDebugLogging)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    _devices.Add(uuid, list = new List<UpnpDevice>());
 | 
					                    _logger.Debug("{0} - SSDP Received a datagram", endpoint);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                using (var reader = new StreamReader(new MemoryStream(received), Encoding.ASCII))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var proto = (reader.ReadLine() ?? string.Empty).Trim();
 | 
				
			||||||
 | 
					                    var method = proto.Split(new[] { ' ' }, 2)[0];
 | 
				
			||||||
 | 
					                    var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
				
			||||||
 | 
					                    for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        line = line.Trim();
 | 
				
			||||||
 | 
					                        if (string.IsNullOrEmpty(line))
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        var parts = line.Split(new[] { ':' }, 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (parts.Length >= 2)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            headers[parts[0]] = parts[1].Trim();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (_config.Configuration.DlnaOptions.EnableDebugLogging)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        _logger.Debug("{0} - Datagram method: {1}", endpoint, method);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    OnMessageReceived(new SsdpMessageEventArgs
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Method = method,
 | 
				
			||||||
 | 
					                        Headers = headers,
 | 
				
			||||||
 | 
					                        EndPoint = (IPEndPoint)endpoint
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception ex)
 | 
				
			||||||
            foreach (var t in new[]
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                "upnp:rootdevice", 
 | 
					                _logger.ErrorException("Failed to read SSDP message", ex);
 | 
				
			||||||
                "urn:schemas-upnp-org:device:MediaServer:1", 
 | 
					 | 
				
			||||||
                "urn:schemas-upnp-org:service:ContentDirectory:1", 
 | 
					 | 
				
			||||||
                "uuid:" + uuid
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                list.Add(new UpnpDevice(uuid, t, descriptor, address));
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            NotifyAll();
 | 
					            if (_socket != null)
 | 
				
			||||||
            _logger.Debug("Registered mount {0} at {1}", uuid, descriptor);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void UnregisterNotification(Guid uuid)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            List<UpnpDevice> dl;
 | 
					 | 
				
			||||||
            lock (_devices)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!_devices.TryGetValue(uuid, out dl))
 | 
					                Receive();
 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                _devices.Remove(uuid);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            foreach (var d in dl)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                NotifyDevice(d, "byebye", true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            _logger.Debug("Unregistered mount {0}", uuid);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void Dispose()
 | 
					        public void Dispose()
 | 
				
			||||||
@ -319,27 +280,20 @@ namespace MediaBrowser.Dlna.Server
 | 
				
			|||||||
                _datagramPosted.WaitOne();
 | 
					                _datagramPosted.WaitOne();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _udpClient.DropMulticastGroup(_ssdpIp);
 | 
					            DisposeSocket();
 | 
				
			||||||
            _udpClient.Close();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            DisposeNotificationTimer();
 | 
					 | 
				
			||||||
            DisposeQueueTimer();
 | 
					            DisposeQueueTimer();
 | 
				
			||||||
 | 
					            DisposeNotificationTimer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _datagramPosted.Dispose();
 | 
					            _datagramPosted.Dispose();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly object _queueTimerSyncLock = new object();
 | 
					        private void DisposeSocket()
 | 
				
			||||||
        private void StartQueueTimer()
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            lock (_queueTimerSyncLock)
 | 
					            if (_socket != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (_queueTimer == null)
 | 
					                _socket.Close();
 | 
				
			||||||
                {
 | 
					                _socket.Dispose();
 | 
				
			||||||
                    _queueTimer = new Timer(QueueTimerCallback, null, 1000, Timeout.Infinite);
 | 
					                _socket = null;
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _queueTimer.Change(1000, Timeout.Infinite);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -355,6 +309,86 @@ namespace MediaBrowser.Dlna.Server
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private Socket CreateMulticastSocket()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
 | 
				
			||||||
 | 
					            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
 | 
				
			||||||
 | 
					            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
 | 
				
			||||||
 | 
					            socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
 | 
				
			||||||
 | 
					            socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(_ssdpIp, 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            socket.Bind(new IPEndPoint(IPAddress.Any, SSDPPort));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return socket;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void NotifyAll()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _logger.Debug("Sending alive notifications");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            foreach (var d in RegisteredDevices)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                NotifyDevice(d, "alive");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void NotifyDevice(UpnpDevice dev, string type, int sendCount = 1)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            const string header = "NOTIFY * HTTP/1.1";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // If needed later for non-server devices, these headers will need to be dynamic 
 | 
				
			||||||
 | 
					            values["HOST"] = "239.255.255.250:1900";
 | 
				
			||||||
 | 
					            values["CACHE-CONTROL"] = "max-age = 600";
 | 
				
			||||||
 | 
					            values["LOCATION"] = dev.Descriptor.ToString();
 | 
				
			||||||
 | 
					            values["SERVER"] = _serverSignature;
 | 
				
			||||||
 | 
					            values["NTS"] = "ssdp:" + type;
 | 
				
			||||||
 | 
					            values["NT"] = dev.Type;
 | 
				
			||||||
 | 
					            values["USN"] = dev.USN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _logger.Debug("{0} said {1}", dev.USN, type);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            SendDatagram(header, values, dev.Address, sendCount);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void RegisterNotification(Guid uuid, Uri descriptionUri, IPAddress address, IEnumerable<string> services)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            List<UpnpDevice> list;
 | 
				
			||||||
 | 
					            lock (_devices)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (!_devices.TryGetValue(uuid, out list))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _devices.TryAdd(uuid, list = new List<UpnpDevice>());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            list.AddRange(services.Select(i => new UpnpDevice(uuid, i, descriptionUri, address)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            NotifyAll();
 | 
				
			||||||
 | 
					            _logger.Debug("Registered mount {0} at {1}", uuid, descriptionUri);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void UnregisterNotification(Guid uuid)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            List<UpnpDevice> dl;
 | 
				
			||||||
 | 
					            if (_devices.TryRemove(uuid, out dl))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                foreach (var d in dl.ToList())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    NotifyDevice(d, "byebye", 2);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _logger.Debug("Unregistered mount {0}", uuid);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly object _notificationTimerSyncLock = new object();
 | 
					        private readonly object _notificationTimerSyncLock = new object();
 | 
				
			||||||
        private void StartNotificationTimer()
 | 
					        private void StartNotificationTimer()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -7,24 +7,6 @@ namespace MediaBrowser.Dlna.Ssdp
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class SsdpHelper
 | 
					    public class SsdpHelper
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private const string SsdpRenderer = "M-SEARCH * HTTP/1.1\r\n" +
 | 
					 | 
				
			||||||
                                             "HOST: 239.255.255.250:1900\r\n" +
 | 
					 | 
				
			||||||
                                             "User-Agent: UPnP/1.0 DLNADOC/1.50 Platinum/0.6.9.1\r\n" +
 | 
					 | 
				
			||||||
                                             "ST: urn:schemas-upnp-org:device:MediaRenderer:1\r\n" +
 | 
					 | 
				
			||||||
                                             "MAN: \"ssdp:discover\"\r\n" +
 | 
					 | 
				
			||||||
                                             "MX: {0}\r\n" +
 | 
					 | 
				
			||||||
                                             "\r\n";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Creates a SSDP MSearch packet for DlnaRenderers.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="mx">The mx. (Delaytime for device before responding)</param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static byte[] CreateRendererSSDP(int mx)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return Encoding.UTF8.GetBytes(string.Format(SsdpRenderer, mx));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Parses the socket response into a location Uri for the DeviceDescription.xml.
 | 
					        /// Parses the socket response into a location Uri for the DeviceDescription.xml.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										47
									
								
								MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace MediaBrowser.Dlna.Ssdp
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class SsdpMessageBuilder
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public string BuildMessage(string header, Dictionary<string, string> values)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var builder = new StringBuilder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const string argFormat = "{0}: {1}\r\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            builder.AppendFormat("{0}\r\n", header);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var pair in values)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.AppendFormat(argFormat, pair.Key, pair.Value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            builder.Append("\r\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return builder.ToString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string BuildDiscoveryMessage(string deviceSearchType, string mx)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            const string header = "M-SEARCH * HTTP/1.1";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            values["HOST"] = "239.255.255.250:1900";
 | 
				
			||||||
 | 
					            values["USER-AGENT"] = "UPnP/1.0 DLNADOC/1.50 Platinum/0.6.9.1";
 | 
				
			||||||
 | 
					            values["ST"] = deviceSearchType;
 | 
				
			||||||
 | 
					            values["MAN"] = "\"ssdp:discover\"";
 | 
				
			||||||
 | 
					            values["MX"] = mx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return BuildMessage(header, values);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string BuildRendererDiscoveryMessage()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return BuildDiscoveryMessage("urn:schemas-upnp-org:device:MediaRenderer:1", "3");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								MediaBrowser.Dlna/Ssdp/SsdpMessageEventArgs.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								MediaBrowser.Dlna/Ssdp/SsdpMessageEventArgs.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace MediaBrowser.Dlna.Ssdp
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class SsdpMessageEventArgs
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public string Method { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IPEndPoint EndPoint { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Dictionary<string, string> Headers { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public SsdpMessageEventArgs()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -298,7 +298,7 @@ namespace MediaBrowser.Model.Dlna
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (IsDirectStream)
 | 
					                if (IsDirectStream)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return MediaSource.Bitrate;
 | 
					                    return MediaSource.Size;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (RunTimeTicks.HasValue)
 | 
					                if (RunTimeTicks.HasValue)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user