aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/Dlna/DlnaServerService.cs3
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs6
-rw-r--r--MediaBrowser.Controller/Dlna/ControlRequest.cs2
-rw-r--r--MediaBrowser.Dlna/Didl/DidlBuilder.cs11
-rw-r--r--MediaBrowser.Dlna/DlnaManager.cs21
-rw-r--r--MediaBrowser.Dlna/Main/DlnaEntryPoint.cs (renamed from MediaBrowser.Dlna/Server/DlnaServerEntryPoint.cs)137
-rw-r--r--MediaBrowser.Dlna/MediaBrowser.Dlna.csproj8
-rw-r--r--MediaBrowser.Dlna/PlayTo/DlnaController.cs2
-rw-r--r--MediaBrowser.Dlna/PlayTo/PlayToManager.cs22
-rw-r--r--MediaBrowser.Dlna/Profiles/LgTvProfile.cs5
-rw-r--r--MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs12
-rw-r--r--MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs5
-rw-r--r--MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs5
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml4
-rw-r--r--MediaBrowser.Dlna/Server/ContentDirectory.cs6
-rw-r--r--MediaBrowser.Dlna/Ssdp/Datagram.cs (renamed from MediaBrowser.Dlna/Server/Datagram.cs)31
-rw-r--r--MediaBrowser.Dlna/Ssdp/SsdpHandler.cs (renamed from MediaBrowser.Dlna/Server/SsdpHandler.cs)410
-rw-r--r--MediaBrowser.Dlna/Ssdp/SsdpHelper.cs18
-rw-r--r--MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs47
-rw-r--r--MediaBrowser.Dlna/Ssdp/SsdpMessageEventArgs.cs20
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs2
21 files changed, 468 insertions, 309 deletions
diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs
index b357409c3..05d41c18b 100644
--- a/MediaBrowser.Api/Dlna/DlnaServerService.cs
+++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs
@@ -93,7 +93,8 @@ namespace MediaBrowser.Api.Dlna
{
Headers = GetRequestHeaders(),
InputXml = await reader.ReadToEndAsync().ConfigureAwait(false),
- TargetServerUuId = id
+ TargetServerUuId = id,
+ RequestedUrl = Request.AbsoluteUri
});
}
}
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index c7a62a332..38d734ff9 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -1,16 +1,16 @@
-using System.Globalization;
-using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Threading;
-using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Api.Playback
{
diff --git a/MediaBrowser.Controller/Dlna/ControlRequest.cs b/MediaBrowser.Controller/Dlna/ControlRequest.cs
index a2b9f7a92..1bb5ddf8a 100644
--- a/MediaBrowser.Controller/Dlna/ControlRequest.cs
+++ b/MediaBrowser.Controller/Dlna/ControlRequest.cs
@@ -10,6 +10,8 @@ namespace MediaBrowser.Controller.Dlna
public string TargetServerUuId { get; set; }
+ public string RequestedUrl { get; set; }
+
public ControlRequest()
{
Headers = new Dictionary<string, string>();
diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs
index 32b49ef9c..e5eff9045 100644
--- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs
+++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs
@@ -18,7 +18,7 @@ namespace MediaBrowser.Dlna.Didl
public class DidlBuilder
{
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_DC = "http://purl.org/dc/elements/1.1/";
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
@@ -298,7 +298,7 @@ namespace MediaBrowser.Dlna.Didl
container.AppendChild(res);
}
-
+
public XmlElement GetFolderElement(XmlDocument doc, Folder folder, int childCount, Filter filter)
{
var container = doc.CreateElement(string.Empty, "container", NS_DIDL);
@@ -450,9 +450,14 @@ namespace MediaBrowser.Dlna.Didl
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)
{
- 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);
}
}
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index 963c68d9a..1f2d5b9c9 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -25,10 +25,10 @@ namespace MediaBrowser.Dlna
private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer;
- public DlnaManager(IXmlSerializer xmlSerializer,
- IFileSystem fileSystem,
- IApplicationPaths appPaths,
- ILogger logger,
+ public DlnaManager(IXmlSerializer xmlSerializer,
+ IFileSystem fileSystem,
+ IApplicationPaths appPaths,
+ ILogger logger,
IJsonSerializer jsonSerializer)
{
_xmlSerializer = xmlSerializer;
@@ -230,6 +230,19 @@ namespace MediaBrowser.Dlna
{
_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;
}
diff --git a/MediaBrowser.Dlna/Server/DlnaServerEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
index d7a34c699..3746630be 100644
--- a/MediaBrowser.Dlna/Server/DlnaServerEntryPoint.cs
+++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
@@ -3,81 +3,102 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Dlna.Ssdp;
using MediaBrowser.Model.Logging;
using System;
-using System.Linq;
+using System.Collections.Generic;
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 ILogger _logger;
-
- private SsdpHandler _ssdpHandler;
private readonly IApplicationHost _appHost;
private readonly INetworkManager _network;
- public static DlnaServerEntryPoint Instance;
+ private SsdpHandler _ssdpHandler;
- public DlnaServerEntryPoint(IServerConfigurationManager config, ILogManager logManager, IApplicationHost appHost, INetworkManager network)
- {
- Instance = this;
+ private readonly List<Guid> _registeredServerIds = new List<Guid>();
+ private bool _dlnaServerStarted;
+ public DlnaEntryPoint(IServerConfigurationManager config, ILogManager logManager, IApplicationHost appHost, INetworkManager network)
+ {
_config = config;
_appHost = appHost;
_network = network;
- _logger = logManager.GetLogger("DlnaServer");
+ _logger = logManager.GetLogger("Dlna");
}
public void Run()
{
- _config.ConfigurationUpdated += ConfigurationUpdated;
+ StartSsdpHandler();
+ ReloadComponents();
- ReloadServer();
+ _config.ConfigurationUpdated += ConfigurationUpdated;
}
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)
{
- StartServer();
+ StartDlnaServer();
}
else if (!_config.Configuration.DlnaOptions.EnableServer && isStarted)
{
- DisposeServer();
+ DisposeDlnaServer();
}
}
- private readonly object _syncLock = new object();
- private void StartServer()
+ private void StartSsdpHandler()
{
- var signature = GenerateServerSignature();
+ try
+ {
+ _ssdpHandler = new SsdpHandler(_logger, _config, GenerateServerSignature());
- lock (_syncLock)
+ _ssdpHandler.Start();
+ }
+ catch (Exception ex)
{
- try
- {
- _ssdpHandler = new SsdpHandler(_logger, _config, signature);
+ _logger.ErrorException("Error starting Dlna server", ex);
+ }
+ }
- RegisterEndpoints();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error starting Dlna server", ex);
- }
+ private void DisposeSsdpHandler()
+ {
+ try
+ {
+ _ssdpHandler.Dispose();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error disposing ssdp handler", ex);
}
}
- private void RegisterEndpoints()
+ 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())
{
@@ -87,31 +108,17 @@ namespace MediaBrowser.Dlna.Server
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>
+ {
+ "upnp:rootdevice",
+ "urn:schemas-upnp-org:device:MediaServer:1",
+ "urn:schemas-upnp-org:service:ContentDirectory:1",
+ "uuid:" + guid.ToString("N")
+ };
- public UpnpDevice GetServerUpnpDevice(string uuid)
- {
- return _ssdpHandler.Devices.FirstOrDefault(i => string.Equals(uuid, i.Uuid.ToString("N"), StringComparison.OrdinalIgnoreCase));
- }
+ _ssdpHandler.RegisterNotification(guid, uri, IPAddress.Parse(address), services);
- private void DisposeServer()
- {
- lock (_syncLock)
- {
- if (_ssdpHandler != null)
- {
- try
- {
- _ssdpHandler.Dispose();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error disposing Dlna server", ex);
- }
- _ssdpHandler = null;
- }
+ _registeredServerIds.Add(guid);
}
}
@@ -140,7 +147,27 @@ namespace MediaBrowser.Dlna.Server
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;
}
}
}
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
index a96838a4d..97da7b697 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
@@ -54,6 +54,7 @@
<Compile Include="DlnaManager.cs" />
<Compile Include="Common\Argument.cs" />
<Compile Include="Eventing\EventManager.cs" />
+ <Compile Include="Main\DlnaEntryPoint.cs" />
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
<Compile Include="PlayTo\Device.cs">
<SubType>Code</SubType>
@@ -79,7 +80,7 @@
<Compile Include="Server\ControlHandler.cs" />
<Compile Include="Server\ServiceActionListBuilder.cs" />
<Compile Include="Server\ContentDirectoryXmlBuilder.cs" />
- <Compile Include="Server\Datagram.cs" />
+ <Compile Include="Ssdp\Datagram.cs" />
<Compile Include="Server\DescriptionXmlBuilder.cs" />
<Compile Include="Ssdp\SsdpHelper.cs" />
<Compile Include="PlayTo\SsdpHttpClient.cs" />
@@ -108,10 +109,11 @@
<Compile Include="Profiles\Xbox360Profile.cs" />
<Compile Include="Profiles\XboxOneProfile.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Server\DlnaServerEntryPoint.cs" />
<Compile Include="Server\Headers.cs" />
- <Compile Include="Server\SsdpHandler.cs" />
<Compile Include="Server\UpnpDevice.cs" />
+ <Compile Include="Ssdp\SsdpMessageBuilder.cs" />
+ <Compile Include="Ssdp\SsdpMessageEventArgs.cs" />
+ <Compile Include="Ssdp\SsdpHandler.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs
index 6b6152d85..11514fb85 100644
--- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs
+++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs
@@ -50,7 +50,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (_device == null || _device.UpdateTime == default(DateTime))
return false;
- return DateTime.UtcNow <= _device.UpdateTime.AddSeconds(30);
+ return DateTime.UtcNow <= _device.UpdateTime.AddMinutes(10);
}
}
diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
index 10e82a227..020bd2116 100644
--- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Net;
+using System.Text;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
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);
- if (!network.SupportsMulticast || !network.GetIPProperties().MulticastAddresses.Any())
+ if (!network.SupportsMulticast || OperationalStatus.Up != network.OperationalStatus || !network.GetIPProperties().MulticastAddresses.Any())
continue;
var ipV4 = network.GetIPProperties().GetIPv4Properties();
@@ -84,7 +85,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
try
{
- CreateListener(localIp);
+ CreateListener(localIp, ipV4.Index);
}
catch (Exception e)
{
@@ -111,15 +112,15 @@ namespace MediaBrowser.Dlna.PlayTo
/// Creates a socket for the interface and listends for data.
/// </summary>
/// <param name="localIp">The local ip.</param>
- private void CreateListener(IPAddress localIp)
+ private void CreateListener(IPAddress localIp, int networkInterfaceIndex)
{
Task.Factory.StartNew(async (o) =>
{
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");
@@ -183,7 +184,8 @@ namespace MediaBrowser.Dlna.PlayTo
{
try
{
- var request = SsdpHelper.CreateRendererSSDP(3);
+ var msg = new SsdpMessageBuilder().BuildRendererDiscoveryMessage();
+ var request = Encoding.UTF8.GetBytes(msg);
while (true)
{
@@ -210,12 +212,12 @@ namespace MediaBrowser.Dlna.PlayTo
/// Gets a socket configured for SDDP multicasting.
/// </summary>
/// <returns></returns>
- private Socket GetMulticastSocket()
+ private Socket GetMulticastSocket(int networkInterfaceIndex)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
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.MulticastTimeToLive, 3);
+ socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), networkInterfaceIndex));
+ socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
return socket;
}
diff --git a/MediaBrowser.Dlna/Profiles/LgTvProfile.cs b/MediaBrowser.Dlna/Profiles/LgTvProfile.cs
index cce33ae10..4ecfd3c5b 100644
--- a/MediaBrowser.Dlna/Profiles/LgTvProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/LgTvProfile.cs
@@ -1,6 +1,5 @@
-using System.Xml.Serialization;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Dlna;
+using System.Xml.Serialization;
namespace MediaBrowser.Dlna.Profiles
{
diff --git a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
index 30f62e81c..d9581ff2c 100644
--- a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
@@ -14,7 +14,17 @@ namespace MediaBrowser.Dlna.Profiles
Identification = new DeviceIdentification
{
- ModelUrl = "samsung.com"
+ ModelUrl = "samsung.com",
+
+ Headers = new[]
+ {
+ new HttpHeaderInfo
+ {
+ Name = "User-Agent",
+ Value = @"SEC_",
+ Match = HeaderMatchType.Substring
+ }
+ }
};
XmlRootAttributes = new[]
diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs
index 01ee7fd68..baaccba5a 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs
+++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs
@@ -1,6 +1,5 @@
-using System.Xml.Serialization;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Dlna;
+using System.Xml.Serialization;
namespace MediaBrowser.Dlna.Profiles
{
diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs
index 6167f553c..389772495 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs
+++ b/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs
@@ -1,6 +1,5 @@
-using System.Xml.Serialization;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Dlna;
+using System.Xml.Serialization;
namespace MediaBrowser.Dlna.Profiles
{
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
index b99ecf872..7b281abd1 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
@@ -3,7 +3,9 @@
<Name>Samsung Smart TV</Name>
<Identification>
<ModelUrl>samsung.com</ModelUrl>
- <Headers />
+ <Headers>
+ <HttpHeaderInfo name="User-Agent" value="SEC_" match="Substring" />
+ </Headers>
</Identification>
<FriendlyName>Media Browser</FriendlyName>
<Manufacturer>Media Browser</Manufacturer>
diff --git a/MediaBrowser.Dlna/Server/ContentDirectory.cs b/MediaBrowser.Dlna/Server/ContentDirectory.cs
index c5b336090..e657a2ff6 100644
--- a/MediaBrowser.Dlna/Server/ContentDirectory.cs
+++ b/MediaBrowser.Dlna/Server/ContentDirectory.cs
@@ -67,10 +67,8 @@ namespace MediaBrowser.Dlna.Server
var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile();
- var device = DlnaServerEntryPoint.Instance.GetServerUpnpDevice(request.TargetServerUuId);
-
- var serverAddress = device.Descriptor.ToString().Substring(0, device.Descriptor.ToString().IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
-
+ var serverAddress = request.RequestedUrl.Substring(0, request.RequestedUrl.IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
+
var user = GetUser(profile);
return new ControlHandler(
diff --git a/MediaBrowser.Dlna/Server/Datagram.cs b/MediaBrowser.Dlna/Ssdp/Datagram.cs
index 2432cbd06..0caf5c78f 100644
--- a/MediaBrowser.Dlna/Server/Datagram.cs
+++ b/MediaBrowser.Dlna/Ssdp/Datagram.cs
@@ -4,24 +4,31 @@ using System.Net;
using System.Net.Sockets;
using System.Text;
-namespace MediaBrowser.Dlna.Server
+namespace MediaBrowser.Dlna.Ssdp
{
public class Datagram
{
public IPEndPoint EndPoint { get; private set; }
public IPAddress LocalAddress { 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; }
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;
_logger = logger;
- Sticky = sticky;
+ TotalSendCount = totalSendCount;
LocalAddress = localAddress;
EndPoint = endPoint;
}
@@ -31,9 +38,11 @@ namespace MediaBrowser.Dlna.Server
var msg = Encoding.ASCII.GetBytes(Message);
try
{
- var client = new UdpClient();
- client.Client.Bind(new IPEndPoint(LocalAddress, 0));
- client.BeginSend(msg, msg.Length, EndPoint, result =>
+ var client = CreateSocket();
+
+ client.Bind(new IPEndPoint(LocalAddress, 0));
+
+ client.BeginSendTo(msg, 0, msg.Length, SocketFlags.None, EndPoint, result =>
{
try
{
@@ -61,5 +70,13 @@ namespace MediaBrowser.Dlna.Server
}
++SendCount;
}
+
+ private Socket CreateSocket()
+ {
+ var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+
+ socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ return socket;
+ }
}
}
diff --git a/MediaBrowser.Dlna/Server/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
index 0430f6a02..01393c6ce 100644
--- a/MediaBrowser.Dlna/Server/SsdpHandler.cs
+++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Dlna.Server;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Concurrent;
@@ -10,72 +11,199 @@ using System.Net.Sockets;
using System.Text;
using System.Threading;
-namespace MediaBrowser.Dlna.Server
+namespace MediaBrowser.Dlna.Ssdp
{
public class SsdpHandler : IDisposable
{
- private readonly AutoResetEvent _datagramPosted = new AutoResetEvent(false);
- private readonly ConcurrentQueue<Datagram> _messageQueue = new ConcurrentQueue<Datagram>();
+ private Socket _socket;
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
- private readonly string _serverSignature;
- private bool _isDisposed;
const string SSDPAddr = "239.255.255.250";
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 UdpClient _udpClient;
-
- private readonly Dictionary<Guid, List<UpnpDevice>> _devices = new Dictionary<Guid, List<UpnpDevice>>();
+ private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort);
private Timer _queueTimer;
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)
{
_logger = logger;
_config = config;
_serverSignature = serverSignature;
+ }
+
+ public event EventHandler<SsdpMessageEventArgs> MessageReceived;
- Start();
+ private void OnMessageReceived(SsdpMessageEventArgs args)
+ {
+ if (string.Equals(args.Method, "M-SEARCH", StringComparison.OrdinalIgnoreCase))
+ {
+ RespondToSearch(args.EndPoint, args.Headers["st"]);
+ }
}
- public IEnumerable<UpnpDevice> Devices
+ public IEnumerable<UpnpDevice> RegisteredDevices
{
get
{
- UpnpDevice[] devs;
- lock (_devices)
- {
- devs = _devices.Values.SelectMany(i => i).ToArray();
- }
- return devs;
+ return _devices.Values.SelectMany(i => i).ToList();
}
}
-
- private void Start()
+
+ public void Start()
{
- _udpClient = new UdpClient();
- _udpClient.Client.UseOnlyOverlappedIO = true;
- _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
- _udpClient.ExclusiveAddressUse = false;
- _udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, SSDPPort));
- _udpClient.JoinMulticastGroup(_ssdpIp, 2);
+ _socket = CreateMulticastSocket();
+
_logger.Info("SSDP service started");
Receive();
StartNotificationTimer();
}
+ public void SendDatagram(string header,
+ Dictionary<string, string> values,
+ IPAddress localAddress,
+ int sendCount = 1)
+ {
+ 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)
+ {
+ dgram.Send();
+ return;
+ }
+
+ _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 RespondToSearch(IPEndPoint endpoint, string deviceType)
+ {
+ if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+ {
+ _logger.Debug("RespondToSearch");
+ }
+
+ const string header = "HTTP/1.1 200 OK";
+
+ foreach (var d in RegisteredDevices)
+ {
+ if (string.Equals(deviceType, "ssdp:all", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(deviceType, d.Type, StringComparison.OrdinalIgnoreCase))
+ {
+ 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());
+ }
+ }
+ }
+
+ private readonly object _queueTimerSyncLock = new object();
+ private void StartQueueTimer()
+ {
+ lock (_queueTimerSyncLock)
+ {
+ if (_queueTimer == null)
+ {
+ _queueTimer = new Timer(QueueTimerCallback, null, 1000, Timeout.Infinite);
+ }
+ else
+ {
+ _queueTimer.Change(1000, Timeout.Infinite);
+ }
+ }
+ }
+
+ private void QueueTimerCallback(object state)
+ {
+ while (_messageQueue.Count != 0)
+ {
+ Datagram msg;
+ if (!_messageQueue.TryPeek(out msg))
+ {
+ continue;
+ }
+
+ if (msg != null && (!_isDisposed || msg.TotalSendCount > 1))
+ {
+ msg.Send();
+ if (msg.SendCount > msg.TotalSendCount)
+ {
+ _messageQueue.TryDequeue(out msg);
+ }
+ break;
+ }
+
+ _messageQueue.TryDequeue(out msg);
+ }
+
+ _datagramPosted.Set();
+
+ if (_messageQueue.Count > 0)
+ {
+ StartQueueTimer();
+ }
+ else
+ {
+ DisposeQueueTimer();
+ }
+ }
+
private void Receive()
{
try
{
- _udpClient.BeginReceive(ReceiveCallback, null);
+ var buffer = new byte[1024];
+
+ EndPoint endpoint = new IPEndPoint(IPAddress.Any, SSDPPort);
+
+ _socket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref endpoint, ReceiveCallback, buffer);
}
catch (ObjectDisposedException)
{
@@ -84,10 +212,16 @@ namespace MediaBrowser.Dlna.Server
private void ReceiveCallback(IAsyncResult result)
{
+ if (_isDisposed)
+ {
+ return;
+ }
+
try
{
- var endpoint = new IPEndPoint(IPAddress.None, SSDPPort);
- var received = _udpClient.EndReceive(result, ref endpoint);
+ EndPoint endpoint = new IPEndPoint(IPAddress.Any, SSDPPort);
+ var receivedCount = _socket.EndReceiveFrom(result, ref endpoint);
+ var received = (byte[])result.AsyncState;
if (_config.Configuration.DlnaOptions.EnableDebugLogging)
{
@@ -98,7 +232,7 @@ namespace MediaBrowser.Dlna.Server
{
var proto = (reader.ReadLine() ?? string.Empty).Trim();
var method = proto.Split(new[] { ' ' }, 2)[0];
- var headers = new Headers();
+ var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
{
line = line.Trim();
@@ -119,10 +253,12 @@ namespace MediaBrowser.Dlna.Server
_logger.Debug("{0} - Datagram method: {1}", endpoint, method);
}
- if (string.Equals(method, "M-SEARCH", StringComparison.OrdinalIgnoreCase))
+ OnMessageReceived(new SsdpMessageEventArgs
{
- RespondToSearch(endpoint, headers["st"]);
- }
+ Method = method,
+ Headers = headers,
+ EndPoint = (IPEndPoint)endpoint
+ });
}
}
catch (Exception ex)
@@ -130,105 +266,60 @@ namespace MediaBrowser.Dlna.Server
_logger.ErrorException("Failed to read SSDP message", ex);
}
- if (!_isDisposed)
+ if (_socket != null)
{
Receive();
}
}
- private void RespondToSearch(IPEndPoint endpoint, string req)
+ public void Dispose()
{
- if (string.Equals(req, "ssdp:all", StringComparison.OrdinalIgnoreCase))
- {
- req = null;
- }
-
- if (_config.Configuration.DlnaOptions.EnableDebugLogging)
- {
- _logger.Debug("RespondToSearch");
- }
-
- foreach (var d in Devices)
+ _isDisposed = true;
+ while (_messageQueue.Count != 0)
{
- if (!string.IsNullOrEmpty(req) && !string.Equals(req, d.Type, StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- SendSearchResponse(endpoint, d);
+ _datagramPosted.WaitOne();
}
- }
-
- private void SendSearchResponse(IPEndPoint endpoint, UpnpDevice dev)
- {
- var builder = new StringBuilder();
-
- 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);
+ DisposeSocket();
+ DisposeQueueTimer();
+ DisposeNotificationTimer();
- _logger.Info("{1} - Responded to a {0} request to {2}", dev.Type, endpoint, dev.Address.ToString());
+ _datagramPosted.Dispose();
}
- private void SendDatagram(IPEndPoint endpoint, IPAddress localAddress, string msg, bool sticky)
+ private void DisposeSocket()
{
- if (_isDisposed)
+ if (_socket != null)
{
- return;
+ _socket.Close();
+ _socket.Dispose();
+ _socket = null;
}
-
- 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 DisposeQueueTimer()
{
- while (_messageQueue.Count != 0)
+ lock (_queueTimerSyncLock)
{
- Datagram msg;
- if (!_messageQueue.TryPeek(out msg))
- {
- continue;
- }
-
- if (msg != null && (!_isDisposed || msg.Sticky))
+ if (_queueTimer != null)
{
- msg.Send();
- if (msg.SendCount > 2)
- {
- _messageQueue.TryDequeue(out msg);
- }
- break;
+ _queueTimer.Dispose();
+ _queueTimer = null;
}
-
- _messageQueue.TryDequeue(out msg);
}
+ }
- _datagramPosted.Set();
+ 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));
- if (_messageQueue.Count > 0)
- {
- StartQueueTimer();
- }
- else
- {
- DisposeQueueTimer();
- }
+ socket.Bind(new IPEndPoint(IPAddress.Any, SSDPPort));
+
+ return socket;
}
private void NotifyAll()
@@ -237,121 +328,64 @@ namespace MediaBrowser.Dlna.Server
{
_logger.Debug("Sending alive notifications");
}
- foreach (var d in Devices)
+ foreach (var d in RegisteredDevices)
{
- NotifyDevice(d, "alive", false);
+ NotifyDevice(d, "alive");
}
}
- private void NotifyDevice(UpnpDevice dev, string type, bool sticky)
+ private void NotifyDevice(UpnpDevice dev, string type, int sendCount = 1)
{
- var builder = new StringBuilder();
+ const string header = "NOTIFY * HTTP/1.1";
- const string argFormat = "{0}: {1}\r\n";
+ var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- 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 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(_ssdpEndp, dev.Address, builder.ToString(), sticky);
+ SendDatagram(header, values, dev.Address, sendCount);
}
- public void RegisterNotification(Guid uuid, Uri descriptor, IPAddress address)
+ public void RegisterNotification(Guid uuid, Uri descriptionUri, IPAddress address, IEnumerable<string> services)
{
List<UpnpDevice> list;
lock (_devices)
{
if (!_devices.TryGetValue(uuid, out list))
{
- _devices.Add(uuid, list = new List<UpnpDevice>());
+ _devices.TryAdd(uuid, list = new List<UpnpDevice>());
}
}
- foreach (var t in new[]
- {
- "upnp:rootdevice",
- "urn:schemas-upnp-org:device:MediaServer:1",
- "urn:schemas-upnp-org:service:ContentDirectory:1",
- "uuid:" + uuid
- })
- {
- list.Add(new UpnpDevice(uuid, t, descriptor, address));
- }
+ list.AddRange(services.Select(i => new UpnpDevice(uuid, i, descriptionUri, address)));
NotifyAll();
- _logger.Debug("Registered mount {0} at {1}", uuid, descriptor);
+ _logger.Debug("Registered mount {0} at {1}", uuid, descriptionUri);
}
- private void UnregisterNotification(Guid uuid)
+ public void UnregisterNotification(Guid uuid)
{
List<UpnpDevice> dl;
- lock (_devices)
+ if (_devices.TryRemove(uuid, out dl))
{
- if (!_devices.TryGetValue(uuid, out dl))
- {
- return;
- }
- _devices.Remove(uuid);
- }
- foreach (var d in dl)
- {
- NotifyDevice(d, "byebye", true);
- }
- _logger.Debug("Unregistered mount {0}", uuid);
- }
-
- public void Dispose()
- {
- _isDisposed = true;
- while (_messageQueue.Count != 0)
- {
- _datagramPosted.WaitOne();
- }
- _udpClient.DropMulticastGroup(_ssdpIp);
- _udpClient.Close();
-
- DisposeNotificationTimer();
- DisposeQueueTimer();
- _datagramPosted.Dispose();
- }
-
- private readonly object _queueTimerSyncLock = new object();
- private void StartQueueTimer()
- {
- lock (_queueTimerSyncLock)
- {
- if (_queueTimer == null)
- {
- _queueTimer = new Timer(QueueTimerCallback, null, 1000, Timeout.Infinite);
- }
- else
+ foreach (var d in dl.ToList())
{
- _queueTimer.Change(1000, Timeout.Infinite);
+ NotifyDevice(d, "byebye", 2);
}
- }
- }
- private void DisposeQueueTimer()
- {
- lock (_queueTimerSyncLock)
- {
- if (_queueTimer != null)
- {
- _queueTimer.Dispose();
- _queueTimer = null;
- }
+ _logger.Debug("Unregistered mount {0}", uuid);
}
}
diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs b/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs
index b22db781a..2b5f38622 100644
--- a/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs
+++ b/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs
@@ -7,24 +7,6 @@ namespace MediaBrowser.Dlna.Ssdp
{
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>
/// Parses the socket response into a location Uri for the DeviceDescription.xml.
/// </summary>
diff --git a/MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs b/MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs
new file mode 100644
index 000000000..4e60cfe2e
--- /dev/null
+++ b/MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs
@@ -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");
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/Ssdp/SsdpMessageEventArgs.cs b/MediaBrowser.Dlna/Ssdp/SsdpMessageEventArgs.cs
new file mode 100644
index 000000000..d6368191b
--- /dev/null
+++ b/MediaBrowser.Dlna/Ssdp/SsdpMessageEventArgs.cs
@@ -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);
+ }
+ }
+}
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 1e8ca6f20..b6bf9b183 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -298,7 +298,7 @@ namespace MediaBrowser.Model.Dlna
{
if (IsDirectStream)
{
- return MediaSource.Bitrate;
+ return MediaSource.Size;
}
if (RunTimeTicks.HasValue)