PlayToManager.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. using MediaBrowser.Common.Net;
  2. using MediaBrowser.Controller.Configuration;
  3. using MediaBrowser.Controller.Dlna;
  4. using MediaBrowser.Controller.Entities;
  5. using MediaBrowser.Controller.Library;
  6. using MediaBrowser.Controller.Persistence;
  7. using MediaBrowser.Controller.Session;
  8. using MediaBrowser.Model.Logging;
  9. using System;
  10. using System.Collections.Concurrent;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Net;
  14. using System.Net.NetworkInformation;
  15. using System.Net.Sockets;
  16. using System.Text;
  17. using System.Threading;
  18. using System.Threading.Tasks;
  19. namespace MediaBrowser.Dlna.PlayTo
  20. {
  21. class PlayToManager : IDisposable
  22. {
  23. private bool _disposed = false;
  24. private readonly ILogger _logger;
  25. private readonly ISessionManager _sessionManager;
  26. private readonly IHttpClient _httpClient;
  27. private readonly CancellationTokenSource _tokenSource;
  28. private ConcurrentDictionary<string, DateTime> _locations;
  29. private readonly IItemRepository _itemRepository;
  30. private readonly ILibraryManager _libraryManager;
  31. private readonly INetworkManager _networkManager;
  32. private readonly IUserManager _userManager;
  33. private readonly IDlnaManager _dlnaManager;
  34. public PlayToManager(ILogger logger,IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepository, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager)
  35. {
  36. _locations = new ConcurrentDictionary<string, DateTime>();
  37. _tokenSource = new CancellationTokenSource();
  38. _logger = logger;
  39. _sessionManager = sessionManager;
  40. _httpClient = httpClient;
  41. _itemRepository = itemRepository;
  42. _libraryManager = libraryManager;
  43. _networkManager = networkManager;
  44. _userManager = userManager;
  45. _dlnaManager = dlnaManager;
  46. var path = Path.Combine(config.CommonApplicationPaths.ConfigurationDirectoryPath, "DlnaProfiles.xml");
  47. }
  48. public async void Start()
  49. {
  50. _logger.Log(LogSeverity.Info, "PlayTo-Manager starting");
  51. _locations = new ConcurrentDictionary<string, DateTime>();
  52. foreach (var network in NetworkInterface.GetAllNetworkInterfaces())
  53. {
  54. _logger.Debug("Found interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
  55. if (!network.SupportsMulticast || OperationalStatus.Up != network.OperationalStatus || !network.GetIPProperties().MulticastAddresses.Any())
  56. continue;
  57. var ipV4 = network.GetIPProperties().GetIPv4Properties();
  58. if (null == ipV4)
  59. continue;
  60. IPAddress localIp = null;
  61. foreach (UnicastIPAddressInformation ipInfo in network.GetIPProperties().UnicastAddresses)
  62. {
  63. if (ipInfo.Address.AddressFamily == AddressFamily.InterNetwork)
  64. {
  65. localIp = ipInfo.Address;
  66. break;
  67. }
  68. }
  69. if (localIp == null)
  70. {
  71. continue;
  72. }
  73. try
  74. {
  75. CreateListener(localIp);
  76. }
  77. catch (Exception e)
  78. {
  79. _logger.ErrorException("Failed to Initilize Socket", e);
  80. }
  81. await Task.Delay(100).ConfigureAwait(false);
  82. }
  83. }
  84. public void Stop()
  85. {
  86. }
  87. /// <summary>
  88. /// Creates a socket for the interface and listends for data.
  89. /// </summary>
  90. /// <param name="localIp">The local ip.</param>
  91. private void CreateListener(IPAddress localIp)
  92. {
  93. Task.Factory.StartNew(async (o) =>
  94. {
  95. try
  96. {
  97. var socket = GetMulticastSocket();
  98. socket.Bind(new IPEndPoint(localIp, 0));
  99. _logger.Info("Creating SSDP listener");
  100. var receiveBuffer = new byte[64000];
  101. CreateNotifier(socket);
  102. while (!_tokenSource.IsCancellationRequested)
  103. {
  104. var receivedBytes = await socket.ReceiveAsync(receiveBuffer, 0, 64000);
  105. if (receivedBytes > 0)
  106. {
  107. var rawData = Encoding.UTF8.GetString(receiveBuffer, 0, receivedBytes);
  108. var uri = SsdpHelper.ParseSsdpResponse(rawData);
  109. TryCreateController(uri);
  110. }
  111. }
  112. _logger.Info("SSDP listener - Task completed");
  113. }
  114. catch (OperationCanceledException c)
  115. {
  116. }
  117. catch (Exception e)
  118. {
  119. _logger.ErrorException("Error in listener", e);
  120. }
  121. }, _tokenSource.Token, TaskCreationOptions.LongRunning);
  122. }
  123. private void TryCreateController(Uri uri)
  124. {
  125. Task.Run(async () =>
  126. {
  127. try
  128. {
  129. await CreateController(uri).ConfigureAwait(false);
  130. }
  131. catch (OperationCanceledException c)
  132. {
  133. }
  134. catch (Exception ex)
  135. {
  136. _logger.ErrorException("Error creating play to controller", ex);
  137. }
  138. });
  139. }
  140. private void CreateNotifier(Socket socket)
  141. {
  142. Task.Factory.StartNew(async (o) =>
  143. {
  144. try
  145. {
  146. var request = SsdpHelper.CreateRendererSSDP(3);
  147. while (true)
  148. {
  149. socket.SendTo(request, new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900));
  150. await Task.Delay(10000).ConfigureAwait(false);
  151. }
  152. }
  153. catch (OperationCanceledException c)
  154. {
  155. }
  156. catch (Exception ex)
  157. {
  158. _logger.ErrorException("Error in notifier", ex);
  159. }
  160. }, _tokenSource.Token, TaskCreationOptions.LongRunning);
  161. }
  162. /// <summary>
  163. /// Gets a socket configured for SDDP multicasting.
  164. /// </summary>
  165. /// <returns></returns>
  166. private Socket GetMulticastSocket()
  167. {
  168. var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  169. socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
  170. socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250")));
  171. //socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 3);
  172. return socket;
  173. }
  174. /// <summary>
  175. /// Creates a new DlnaSessionController.
  176. /// and logs the session in SessionManager
  177. /// </summary>
  178. /// <param name="uri">The URI.</param>
  179. /// <returns></returns>
  180. private async Task CreateController(Uri uri)
  181. {
  182. if (!IsUriValid(uri))
  183. return;
  184. var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _logger).ConfigureAwait(false);
  185. if (device != null && device.RendererCommands != null && !_sessionManager.Sessions.Any(s => string.Equals(s.DeviceId, device.Properties.UUID) && s.IsActive))
  186. {
  187. GetProfileSettings(device.Properties);
  188. var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, device.Properties.Name, device.Properties.UUID, device.Properties.DisplayName, uri.OriginalString, null)
  189. .ConfigureAwait(false);
  190. var controller = sessionInfo.SessionController as PlayToController;
  191. if (controller == null)
  192. {
  193. sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager);
  194. }
  195. controller.Init(device);
  196. _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
  197. }
  198. }
  199. /// <summary>
  200. /// Gets the profile settings.
  201. /// </summary>
  202. /// <param name="deviceProperties">The device properties.</param>
  203. /// <returns>The TranscodeSettings for the device</returns>
  204. private void GetProfileSettings(DeviceInfo deviceProperties)
  205. {
  206. var profile = _dlnaManager.GetProfile(deviceProperties.DisplayName, deviceProperties.ModelName,
  207. deviceProperties.ModelNumber);
  208. deviceProperties.DisplayName = profile.Name;
  209. deviceProperties.ClientType = profile.ClientType;
  210. }
  211. /// <summary>
  212. /// Determines if the Uri is valid for further inspection or not.
  213. /// (the limit for reinspection is 5 minutes)
  214. /// </summary>
  215. /// <param name="uri">The URI.</param>
  216. /// <returns>Returns <b>True</b> if the Uri is valid for further inspection</returns>
  217. private bool IsUriValid(Uri uri)
  218. {
  219. if (uri == null)
  220. return false;
  221. if (!_locations.ContainsKey(uri.OriginalString))
  222. {
  223. _locations.AddOrUpdate(uri.OriginalString, DateTime.UtcNow, (key, existingVal) => existingVal);
  224. return true;
  225. }
  226. var time = _locations[uri.OriginalString];
  227. if ((DateTime.UtcNow - time).TotalMinutes <= 5)
  228. {
  229. return false;
  230. }
  231. return _locations.TryUpdate(uri.OriginalString, DateTime.UtcNow, time);
  232. }
  233. public void Dispose()
  234. {
  235. if (!_disposed)
  236. {
  237. _disposed = true;
  238. _tokenSource.Cancel();
  239. }
  240. }
  241. }
  242. }