2
0

DeviceDiscovery.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. #nullable disable
  2. #pragma warning disable CS1591
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using Jellyfin.Data.Events;
  7. using MediaBrowser.Controller.Configuration;
  8. using MediaBrowser.Model.Dlna;
  9. using Rssdp;
  10. using Rssdp.Infrastructure;
  11. namespace Emby.Dlna.Ssdp
  12. {
  13. public sealed class DeviceDiscovery : IDeviceDiscovery, IDisposable
  14. {
  15. private readonly object _syncLock = new object();
  16. private readonly IServerConfigurationManager _config;
  17. private SsdpDeviceLocator _deviceLocator;
  18. private ISsdpCommunicationsServer _commsServer;
  19. private int _listenerCount;
  20. private bool _disposed;
  21. public DeviceDiscovery(IServerConfigurationManager config)
  22. {
  23. _config = config;
  24. }
  25. private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
  26. /// <inheritdoc />
  27. public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered
  28. {
  29. add
  30. {
  31. lock (_syncLock)
  32. {
  33. _listenerCount++;
  34. DeviceDiscoveredInternal += value;
  35. }
  36. StartInternal();
  37. }
  38. remove
  39. {
  40. lock (_syncLock)
  41. {
  42. _listenerCount--;
  43. DeviceDiscoveredInternal -= value;
  44. }
  45. }
  46. }
  47. /// <inheritdoc />
  48. public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
  49. // Call this method from somewhere in your code to start the search.
  50. public void Start(ISsdpCommunicationsServer communicationsServer)
  51. {
  52. _commsServer = communicationsServer;
  53. StartInternal();
  54. }
  55. private void StartInternal()
  56. {
  57. lock (_syncLock)
  58. {
  59. if (_listenerCount > 0 && _deviceLocator is null && _commsServer is not null)
  60. {
  61. _deviceLocator = new SsdpDeviceLocator(
  62. _commsServer,
  63. Environment.OSVersion.Platform.ToString(),
  64. // Can not use VersionString here since that includes OS and version
  65. Environment.OSVersion.Version.ToString());
  66. // (Optional) Set the filter so we only see notifications for devices we care about
  67. // (can be any search target value i.e device type, uuid value etc - any value that appears in the
  68. // DiscoverdSsdpDevice.NotificationType property or that is used with the searchTarget parameter of the Search method).
  69. // _DeviceLocator.NotificationFilter = "upnp:rootdevice";
  70. // Connect our event handler so we process devices as they are found
  71. _deviceLocator.DeviceAvailable += OnDeviceLocatorDeviceAvailable;
  72. _deviceLocator.DeviceUnavailable += OnDeviceLocatorDeviceUnavailable;
  73. var dueTime = TimeSpan.FromSeconds(5);
  74. var interval = TimeSpan.FromSeconds(_config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds);
  75. _deviceLocator.RestartBroadcastTimer(dueTime, interval);
  76. }
  77. }
  78. }
  79. // Process each found device in the event handler
  80. private void OnDeviceLocatorDeviceAvailable(object sender, DeviceAvailableEventArgs e)
  81. {
  82. var originalHeaders = e.DiscoveredDevice.ResponseHeaders;
  83. var headerDict = originalHeaders is null ? new Dictionary<string, KeyValuePair<string, IEnumerable<string>>>() : originalHeaders.ToDictionary(i => i.Key, StringComparer.OrdinalIgnoreCase);
  84. var headers = headerDict.ToDictionary(i => i.Key, i => i.Value.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase);
  85. var args = new GenericEventArgs<UpnpDeviceInfo>(
  86. new UpnpDeviceInfo
  87. {
  88. Location = e.DiscoveredDevice.DescriptionLocation,
  89. Headers = headers,
  90. RemoteIPAddress = e.RemoteIPAddress
  91. });
  92. DeviceDiscoveredInternal?.Invoke(this, args);
  93. }
  94. private void OnDeviceLocatorDeviceUnavailable(object sender, DeviceUnavailableEventArgs e)
  95. {
  96. var originalHeaders = e.DiscoveredDevice.ResponseHeaders;
  97. var headerDict = originalHeaders is null ? new Dictionary<string, KeyValuePair<string, IEnumerable<string>>>() : originalHeaders.ToDictionary(i => i.Key, StringComparer.OrdinalIgnoreCase);
  98. var headers = headerDict.ToDictionary(i => i.Key, i => i.Value.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase);
  99. var args = new GenericEventArgs<UpnpDeviceInfo>(
  100. new UpnpDeviceInfo
  101. {
  102. Location = e.DiscoveredDevice.DescriptionLocation,
  103. Headers = headers
  104. });
  105. DeviceLeft?.Invoke(this, args);
  106. }
  107. /// <inheritdoc />
  108. public void Dispose()
  109. {
  110. if (!_disposed)
  111. {
  112. _disposed = true;
  113. if (_deviceLocator is not null)
  114. {
  115. _deviceLocator.Dispose();
  116. _deviceLocator = null;
  117. }
  118. }
  119. }
  120. }
  121. }