diff --git a/Emby.Common.Implementations/Net/NetAcceptSocket.cs b/Emby.Common.Implementations/Net/NetAcceptSocket.cs index 0672a9e980..731ad1b741 100644 --- a/Emby.Common.Implementations/Net/NetAcceptSocket.cs +++ b/Emby.Common.Implementations/Net/NetAcceptSocket.cs @@ -47,6 +47,13 @@ namespace Emby.Common.Implementations.Net } } + public void Connect(IpEndPointInfo endPoint) + { + var nativeEndpoint = NetworkManager.ToIPEndPoint(endPoint); + + Socket.Connect(nativeEndpoint); + } + public void Close() { #if NET46 diff --git a/Emby.Common.Implementations/Net/SocketFactory.cs b/Emby.Common.Implementations/Net/SocketFactory.cs index bb964fe00f..523f4da855 100644 --- a/Emby.Common.Implementations/Net/SocketFactory.cs +++ b/Emby.Common.Implementations/Net/SocketFactory.cs @@ -31,7 +31,7 @@ namespace Emby.Common.Implementations.Net _logger = logger; } - public IAcceptSocket CreateAcceptSocket(IpAddressFamily family, MediaBrowser.Model.Net.SocketType socketType, MediaBrowser.Model.Net.ProtocolType protocolType, bool dualMode) + public IAcceptSocket CreateSocket(IpAddressFamily family, MediaBrowser.Model.Net.SocketType socketType, MediaBrowser.Model.Net.ProtocolType protocolType, bool dualMode) { try { diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 32954da35f..c704d0e4e5 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -172,8 +172,8 @@ - - + + diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 2eb02d63c6..c07b6be82d 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -29,14 +29,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly IFileSystem _fileSystem; private readonly IServerApplicationHost _appHost; private readonly ISocketFactory _socketFactory; + private readonly INetworkManager _networkManager; - public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient, IFileSystem fileSystem, IServerApplicationHost appHost, ISocketFactory socketFactory) + public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient, IFileSystem fileSystem, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager) : base(config, logger, jsonSerializer, mediaEncoder) { _httpClient = httpClient; _fileSystem = fileSystem; _appHost = appHost; _socketFactory = socketFactory; + _networkManager = networkManager; } public string Name @@ -89,6 +91,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private class HdHomerunChannelInfo : ChannelInfo { public bool IsLegacyTuner { get; set; } + public string Url { get; set; } } protected override async Task> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) @@ -106,7 +109,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun AudioCodec = i.AudioCodec, VideoCodec = i.VideoCodec, ChannelType = ChannelType.TV, - IsLegacyTuner = (i.URL ?? string.Empty).StartsWith("hdhomerun", StringComparison.OrdinalIgnoreCase) + IsLegacyTuner = (i.URL ?? string.Empty).StartsWith("hdhomerun", StringComparison.OrdinalIgnoreCase), + Url = i.URL }).Cast().ToList(); } @@ -500,7 +504,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var hdhrId = GetHdHrIdFromChannelId(channelId); var channels = await GetChannels(info, true, CancellationToken.None).ConfigureAwait(false); - var channelInfo = channels.FirstOrDefault(i => string.Equals(i.Number, channelId, StringComparison.OrdinalIgnoreCase)); + var channelInfo = channels.FirstOrDefault(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)); var hdHomerunChannelInfo = channelInfo as HdHomerunChannelInfo; @@ -570,24 +574,23 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var hdhrId = GetHdHrIdFromChannelId(channelId); var channels = await GetChannels(info, true, CancellationToken.None).ConfigureAwait(false); - var channelInfo = channels.FirstOrDefault(i => string.Equals(i.Number, channelId, StringComparison.OrdinalIgnoreCase)); + var channelInfo = channels.FirstOrDefault(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)); var hdhomerunChannel = channelInfo as HdHomerunChannelInfo; if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner) { + var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); var mediaSource = GetLegacyMediaSource(info, hdhrId, channelInfo); - var liveStream = new HdHomerunLiveStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); - liveStream.EnableStreamSharing = true; + var liveStream = new HdHomerunUdpStream(mediaSource, streamId, hdhomerunChannel.Url, modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); return liveStream; } else { var mediaSource = GetMediaSource(info, hdhrId, channelInfo, profile); - var liveStream = new HdHomerunLiveStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); - liveStream.EnableStreamSharing = true; + var liveStream = new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); return liveStream; } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs similarity index 97% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs rename to Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs index 625e4457df..2798805fa0 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs @@ -13,7 +13,7 @@ using MediaBrowser.Model.MediaInfo; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { - public class HdHomerunLiveStream : LiveStream, IDirectStreamProvider + public class HdHomerunHttpStream : LiveStream, IDirectStreamProvider { private readonly ILogger _logger; private readonly IHttpClient _httpClient; @@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource(); private readonly MulticastStream _multicastStream; - public HdHomerunLiveStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost) + public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost) : base(mediaSource) { _fileSystem = fileSystem; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunLiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs similarity index 63% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunLiveStream.cs rename to Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 7bb524c49a..95ceb06603 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunLiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -17,7 +17,7 @@ using MediaBrowser.Model.Net; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { - public class LegacyHdHomerunLiveStream : LiveStream, IDirectStreamProvider + public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider { private readonly ILogger _logger; private readonly IHttpClient _httpClient; @@ -33,7 +33,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly int _numTuners; private readonly INetworkManager _networkManager; - public LegacyHdHomerunLiveStream(MediaSourceInfo mediaSource, string originalStreamId, string channelUrl, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager) + public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, string channelUrl, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager) : base(mediaSource) { _fileSystem = fileSystem; @@ -58,7 +58,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var uri = new Uri(mediaSource.Path); var localPort = _networkManager.GetRandomUnusedUdpPort(); - _logger.Info("Opening Legacy HDHR Live stream from {0}", uri.Host); + _logger.Info("Opening HDHR UDP Live stream from {0}", uri.Host); var taskCompletionSource = new TaskCompletionSource(); @@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public override Task Close() { - _logger.Info("Closing Legacy HDHR live stream"); + _logger.Info("Closing HDHR UDP live stream"); _liveStreamCancellationTokenSource.Cancel(); return _liveStreamTaskCompletionSource.Task; @@ -89,74 +89,78 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private async Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) { - //await Task.Run(async () => - //{ - // var isFirstAttempt = true; - // var udpClient = _socketFactory.CreateUdpSocket(localPort); - // using (var legCommand = new HdHomerunManager(_socketFactory)) - // { - // var remoteAddress = new IpAddressInfo(remoteIp, IpAddressFamily.InterNetwork); - // IpAddressInfo localAddress = null; - // var tcpSocket = _socketFactory.CreateSocket(IpAddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp, false); - // try - // { - // tcpSocket.Connect(new IpEndPointInfo(remoteAddress, LegacyHdHomerunCommand.HdHomeRunPort)); - // localAddress = tcpSocket.LocalEndPoint.IpAddress; - // tcpSocket.Close(); - // } - // catch (Exception) - // { - // _logger.Error("Unable to determine local ip address for Legacy HDHomerun stream."); - // return; - // } - - // while (!cancellationToken.IsCancellationRequested) - // { - // try - // { - // // send url to start streaming - // await legCommand.StartStreaming(remoteAddress, localAddress, localPort, _channelUrl, _numTuners, cancellationToken).ConfigureAwait(false); - - // var response = await udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); - // _logger.Info("Opened Legacy HDHR stream from {0}", _channelUrl); - - // if (!cancellationToken.IsCancellationRequested) - // { - // Action onStarted = null; - // if (isFirstAttempt) - // { - // onStarted = () => openTaskCompletionSource.TrySetResult(true); - // } - - // var stream = new UdpClientStream(udpClient); - // await _multicastStream.CopyUntilCancelled(stream, onStarted, cancellationToken).ConfigureAwait(false); - // } - // } - // catch (OperationCanceledException) - // { - // break; - // } - // catch (Exception ex) - // { - // if (isFirstAttempt) - // { - // _logger.ErrorException("Error opening live stream:", ex); - // openTaskCompletionSource.TrySetException(ex); - // break; - // } - - // _logger.ErrorException("Error copying live stream, will reopen", ex); - // } - - // isFirstAttempt = false; - // } - - // await legCommand.StopStreaming().ConfigureAwait(false); - // udpClient.Dispose(); - // _liveStreamTaskCompletionSource.TrySetResult(true); - // } - - //}).ConfigureAwait(false); + await Task.Run(async () => + { + var isFirstAttempt = true; + using (var udpClient = _socketFactory.CreateUdpSocket(localPort)) + { + using (var hdHomerunManager = new HdHomerunManager(_socketFactory)) + { + var remoteAddress = new IpAddressInfo(remoteIp, IpAddressFamily.InterNetwork); + IpAddressInfo localAddress = null; + using (var tcpSocket = _socketFactory.CreateSocket(IpAddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp, false)) + { + try + { + tcpSocket.Connect(new IpEndPointInfo(remoteAddress, HdHomerunManager.HdHomeRunPort)); + localAddress = tcpSocket.LocalEndPoint.IpAddress; + tcpSocket.Close(); + } + catch (Exception) + { + _logger.Error("Unable to determine local ip address for Legacy HDHomerun stream."); + return; + } + } + + while (!cancellationToken.IsCancellationRequested) + { + try + { + // send url to start streaming + await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelUrl, _numTuners, cancellationToken).ConfigureAwait(false); + + var response = await udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); + _logger.Info("Opened HDHR UDP stream from {0}", _channelUrl); + + if (!cancellationToken.IsCancellationRequested) + { + Action onStarted = null; + if (isFirstAttempt) + { + onStarted = () => openTaskCompletionSource.TrySetResult(true); + } + + var stream = new UdpClientStream(udpClient); + await _multicastStream.CopyUntilCancelled(stream, onStarted, cancellationToken).ConfigureAwait(false); + } + } + catch (OperationCanceledException) + { + break; + } + catch (Exception ex) + { + if (isFirstAttempt) + { + _logger.ErrorException("Error opening live stream:", ex); + openTaskCompletionSource.TrySetException(ex); + break; + } + + _logger.ErrorException("Error copying live stream, will reopen", ex); + } + + isFirstAttempt = false; + } + + await hdHomerunManager.StopStreaming().ConfigureAwait(false); + udpClient.Dispose(); + _liveStreamTaskCompletionSource.TrySetResult(true); + } + } + + }).ConfigureAwait(false); } public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) diff --git a/MediaBrowser.Model/Net/IAcceptSocket.cs b/MediaBrowser.Model/Net/IAcceptSocket.cs index 0467129c01..cac23b337f 100644 --- a/MediaBrowser.Model/Net/IAcceptSocket.cs +++ b/MediaBrowser.Model/Net/IAcceptSocket.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Model.Net void Shutdown(bool both); void Listen(int backlog); void Bind(IpEndPointInfo endpoint); - + void Connect(IpEndPointInfo endPoint); void StartAccept(Action onAccept, Func isClosed); } diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index e4ef9f6d4f..4b70f3362f 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -30,7 +30,7 @@ namespace MediaBrowser.Model.Net /// A implementation. ISocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort); - IAcceptSocket CreateAcceptSocket(IpAddressFamily family, SocketType socketType, ProtocolType protocolType, bool dualMode); + IAcceptSocket CreateSocket(IpAddressFamily family, SocketType socketType, ProtocolType protocolType, bool dualMode); } public enum SocketType diff --git a/SharedVersion.cs b/SharedVersion.cs index e66950cb59..a3b0bf79ad 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.5.3")] +[assembly: AssemblyVersion("3.2.5.4")] diff --git a/SocketHttpListener.Portable/Net/EndPointListener.cs b/SocketHttpListener.Portable/Net/EndPointListener.cs index 77ed65aff3..c7642d5d1e 100644 --- a/SocketHttpListener.Portable/Net/EndPointListener.cs +++ b/SocketHttpListener.Portable/Net/EndPointListener.cs @@ -67,7 +67,7 @@ namespace SocketHttpListener.Net { try { - sock = _socketFactory.CreateAcceptSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode); + sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode); } catch (SocketCreateException ex) { @@ -78,7 +78,7 @@ namespace SocketHttpListener.Net { endpoint = new IpEndPointInfo(IpAddressInfo.Any, endpoint.Port); _enableDualMode = false; - sock = _socketFactory.CreateAcceptSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode); + sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode); } else {