From 9776ca09db59a1e382045a072813a29cf07cadb3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 2 Mar 2017 15:50:09 -0500 Subject: update socket interfaces --- MediaBrowser.Model/Net/ISocketFactory.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'MediaBrowser.Model/Net/ISocketFactory.cs') diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index ac406e7f1..e4ef9f6d4 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -2,7 +2,7 @@ namespace MediaBrowser.Model.Net { /// - /// Implemented by components that can create a platform specific UDP socket implementation, and wrap it in the cross platform interface. + /// Implemented by components that can create a platform specific UDP socket implementation, and wrap it in the cross platform interface. /// public interface ISocketFactory { @@ -11,13 +11,15 @@ namespace MediaBrowser.Model.Net /// Createa a new unicast socket using the specified local port number. /// /// The local port to bind to. - /// A implementation. - IUdpSocket CreateUdpSocket(int localPort); + /// A implementation. + ISocket CreateUdpSocket(int localPort); + + ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort); /// /// Createa a new unicast socket using the specified local port number. /// - IUdpSocket CreateSsdpUdpSocket(IpAddressInfo localIp, int localPort); + ISocket CreateSsdpUdpSocket(IpAddressInfo localIp, int localPort); /// /// Createa a new multicast socket using the specified multicast IP address, multicast time to live and local port. @@ -25,10 +27,10 @@ namespace MediaBrowser.Model.Net /// The multicast IP address to bind to. /// The multicast time to live value. Actually a maximum number of network hops for UDP packets. /// The local port to bind to. - /// A implementation. - IUdpSocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort); + /// A implementation. + ISocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort); - ISocket CreateSocket(IpAddressFamily family, SocketType socketType, ProtocolType protocolType, bool dualMode); + IAcceptSocket CreateAcceptSocket(IpAddressFamily family, SocketType socketType, ProtocolType protocolType, bool dualMode); } public enum SocketType -- cgit v1.2.3 From 7cbc76af27637fca10bca21d0b343f96b1a02b6a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 3 Mar 2017 00:53:21 -0500 Subject: 3.2.5.4 --- Emby.Common.Implementations/Net/NetAcceptSocket.cs | 7 + Emby.Common.Implementations/Net/SocketFactory.cs | 2 +- .../Emby.Server.Implementations.csproj | 4 +- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 19 +- .../TunerHosts/HdHomerun/HdHomerunHttpStream.cs | 143 ++++++++++ .../TunerHosts/HdHomerun/HdHomerunLiveStream.cs | 143 ---------- .../TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 294 +++++++++++++++++++++ .../HdHomerun/LegacyHdHomerunLiveStream.cs | 290 -------------------- MediaBrowser.Model/Net/IAcceptSocket.cs | 2 +- MediaBrowser.Model/Net/ISocketFactory.cs | 2 +- SharedVersion.cs | 2 +- .../Net/EndPointListener.cs | 4 +- 12 files changed, 463 insertions(+), 449 deletions(-) create mode 100644 Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs delete mode 100644 Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs create mode 100644 Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs delete mode 100644 Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunLiveStream.cs (limited to 'MediaBrowser.Model/Net/ISocketFactory.cs') diff --git a/Emby.Common.Implementations/Net/NetAcceptSocket.cs b/Emby.Common.Implementations/Net/NetAcceptSocket.cs index 0672a9e98..731ad1b74 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 bb964fe00..523f4da85 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 32954da35..c704d0e4e 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 2eb02d63c..c07b6be82 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/HdHomerunHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs new file mode 100644 index 000000000..2798805fa --- /dev/null +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs @@ -0,0 +1,143 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.IO; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; + +namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +{ + public class HdHomerunHttpStream : LiveStream, IDirectStreamProvider + { + private readonly ILogger _logger; + private readonly IHttpClient _httpClient; + private readonly IFileSystem _fileSystem; + private readonly IServerApplicationPaths _appPaths; + private readonly IServerApplicationHost _appHost; + + private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource(); + private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource(); + private readonly MulticastStream _multicastStream; + + public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost) + : base(mediaSource) + { + _fileSystem = fileSystem; + _httpClient = httpClient; + _logger = logger; + _appPaths = appPaths; + _appHost = appHost; + OriginalStreamId = originalStreamId; + _multicastStream = new MulticastStream(_logger); + } + + protected override async Task OpenInternal(CancellationToken openCancellationToken) + { + _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); + + var mediaSource = OriginalMediaSource; + + var url = mediaSource.Path; + + _logger.Info("Opening HDHR Live stream from {0}", url); + + var taskCompletionSource = new TaskCompletionSource(); + + StartStreaming(url, taskCompletionSource, _liveStreamCancellationTokenSource.Token); + + //OpenedMediaSource.Protocol = MediaProtocol.File; + //OpenedMediaSource.Path = tempFile; + //OpenedMediaSource.ReadAtNativeFramerate = true; + + OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; + OpenedMediaSource.Protocol = MediaProtocol.Http; + OpenedMediaSource.SupportsDirectPlay = false; + OpenedMediaSource.SupportsDirectStream = true; + OpenedMediaSource.SupportsTranscoding = true; + + await taskCompletionSource.Task.ConfigureAwait(false); + + //await Task.Delay(5000).ConfigureAwait(false); + } + + public override Task Close() + { + _logger.Info("Closing HDHR live stream"); + _liveStreamCancellationTokenSource.Cancel(); + + return _liveStreamTaskCompletionSource.Task; + } + + private async Task StartStreaming(string url, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) + { + await Task.Run(async () => + { + var isFirstAttempt = true; + + while (!cancellationToken.IsCancellationRequested) + { + try + { + using (var response = await _httpClient.SendAsync(new HttpRequestOptions + { + Url = url, + CancellationToken = cancellationToken, + BufferContent = false, + + // Increase a little bit + TimeoutMs = 30000 + + }, "GET").ConfigureAwait(false)) + { + _logger.Info("Opened HDHR stream from {0}", url); + + if (!cancellationToken.IsCancellationRequested) + { + _logger.Info("Beginning multicastStream.CopyUntilCancelled"); + + Action onStarted = null; + if (isFirstAttempt) + { + onStarted = () => openTaskCompletionSource.TrySetResult(true); + } + + await _multicastStream.CopyUntilCancelled(response.Content, 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; + } + + _liveStreamTaskCompletionSource.TrySetResult(true); + + }).ConfigureAwait(false); + } + + public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) + { + return _multicastStream.CopyToAsync(stream); + } + } +} diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs deleted file mode 100644 index 625e4457d..000000000 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.IO; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.MediaInfo; - -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun -{ - public class HdHomerunLiveStream : LiveStream, IDirectStreamProvider - { - private readonly ILogger _logger; - private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; - private readonly IServerApplicationPaths _appPaths; - private readonly IServerApplicationHost _appHost; - - private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource(); - 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) - : base(mediaSource) - { - _fileSystem = fileSystem; - _httpClient = httpClient; - _logger = logger; - _appPaths = appPaths; - _appHost = appHost; - OriginalStreamId = originalStreamId; - _multicastStream = new MulticastStream(_logger); - } - - protected override async Task OpenInternal(CancellationToken openCancellationToken) - { - _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); - - var mediaSource = OriginalMediaSource; - - var url = mediaSource.Path; - - _logger.Info("Opening HDHR Live stream from {0}", url); - - var taskCompletionSource = new TaskCompletionSource(); - - StartStreaming(url, taskCompletionSource, _liveStreamCancellationTokenSource.Token); - - //OpenedMediaSource.Protocol = MediaProtocol.File; - //OpenedMediaSource.Path = tempFile; - //OpenedMediaSource.ReadAtNativeFramerate = true; - - OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; - OpenedMediaSource.Protocol = MediaProtocol.Http; - OpenedMediaSource.SupportsDirectPlay = false; - OpenedMediaSource.SupportsDirectStream = true; - OpenedMediaSource.SupportsTranscoding = true; - - await taskCompletionSource.Task.ConfigureAwait(false); - - //await Task.Delay(5000).ConfigureAwait(false); - } - - public override Task Close() - { - _logger.Info("Closing HDHR live stream"); - _liveStreamCancellationTokenSource.Cancel(); - - return _liveStreamTaskCompletionSource.Task; - } - - private async Task StartStreaming(string url, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) - { - await Task.Run(async () => - { - var isFirstAttempt = true; - - while (!cancellationToken.IsCancellationRequested) - { - try - { - using (var response = await _httpClient.SendAsync(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = false, - - // Increase a little bit - TimeoutMs = 30000 - - }, "GET").ConfigureAwait(false)) - { - _logger.Info("Opened HDHR stream from {0}", url); - - if (!cancellationToken.IsCancellationRequested) - { - _logger.Info("Beginning multicastStream.CopyUntilCancelled"); - - Action onStarted = null; - if (isFirstAttempt) - { - onStarted = () => openTaskCompletionSource.TrySetResult(true); - } - - await _multicastStream.CopyUntilCancelled(response.Content, 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; - } - - _liveStreamTaskCompletionSource.TrySetResult(true); - - }).ConfigureAwait(false); - } - - public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) - { - return _multicastStream.CopyToAsync(stream); - } - } -} diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs new file mode 100644 index 000000000..95ceb0660 --- /dev/null +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Net; + +namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +{ + public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider + { + private readonly ILogger _logger; + private readonly IHttpClient _httpClient; + private readonly IFileSystem _fileSystem; + private readonly IServerApplicationPaths _appPaths; + private readonly IServerApplicationHost _appHost; + private readonly ISocketFactory _socketFactory; + + private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource(); + private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource(); + private readonly MulticastStream _multicastStream; + private readonly string _channelUrl; + private readonly int _numTuners; + private readonly 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; + _httpClient = httpClient; + _logger = logger; + _appPaths = appPaths; + _appHost = appHost; + _socketFactory = socketFactory; + _networkManager = networkManager; + OriginalStreamId = originalStreamId; + _multicastStream = new MulticastStream(_logger); + _channelUrl = channelUrl; + _numTuners = numTuners; + } + + protected override async Task OpenInternal(CancellationToken openCancellationToken) + { + _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); + + var mediaSource = OriginalMediaSource; + + var uri = new Uri(mediaSource.Path); + var localPort = _networkManager.GetRandomUnusedUdpPort(); + + _logger.Info("Opening HDHR UDP Live stream from {0}", uri.Host); + + var taskCompletionSource = new TaskCompletionSource(); + + StartStreaming(uri.Host, localPort, taskCompletionSource, _liveStreamCancellationTokenSource.Token); + + //OpenedMediaSource.Protocol = MediaProtocol.File; + //OpenedMediaSource.Path = tempFile; + //OpenedMediaSource.ReadAtNativeFramerate = true; + + OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; + OpenedMediaSource.Protocol = MediaProtocol.Http; + OpenedMediaSource.SupportsDirectPlay = false; + OpenedMediaSource.SupportsDirectStream = true; + OpenedMediaSource.SupportsTranscoding = true; + + await taskCompletionSource.Task.ConfigureAwait(false); + + //await Task.Delay(5000).ConfigureAwait(false); + } + + public override Task Close() + { + _logger.Info("Closing HDHR UDP live stream"); + _liveStreamCancellationTokenSource.Cancel(); + + return _liveStreamTaskCompletionSource.Task; + } + + private async Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) + { + 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) + { + return _multicastStream.CopyToAsync(stream); + } + } + + // This handles the ReadAsync function only of a Stream object + // This is used to wrap a UDP socket into a stream for MulticastStream which only uses ReadAsync + public class UdpClientStream : Stream + { + private static int RtpHeaderBytes = 12; + private static int PacketSize = 1316; + private readonly ISocket _udpClient; + bool disposed; + + public UdpClientStream(ISocket udpClient) : base() + { + _udpClient = udpClient; + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + + if (offset + count < 0) + throw new ArgumentOutOfRangeException("offset + count must not be negative", "offset+count"); + + if (offset + count > buffer.Length) + throw new ArgumentException("offset + count must not be greater than the length of buffer", "offset+count"); + + if (disposed) + throw new ObjectDisposedException(typeof(UdpClientStream).ToString()); + + // This will always receive a 1328 packet size (PacketSize + RtpHeaderSize) + // The RTP header will be stripped so see how many reads we need to make to fill the buffer. + int numReads = count / PacketSize; + int totalBytesRead = 0; + + for (int i = 0; i < numReads; ++i) + { + var data = await _udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); + + var bytesRead = data.ReceivedBytes - RtpHeaderBytes; + + // remove rtp header + Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead); + offset += bytesRead; + totalBytesRead += bytesRead; + } + return totalBytesRead; + } + + protected override void Dispose(bool disposing) + { + disposed = true; + } + + public override bool CanRead + { + get + { + throw new NotImplementedException(); + } + } + + public override bool CanSeek + { + get + { + throw new NotImplementedException(); + } + } + + public override bool CanWrite + { + get + { + throw new NotImplementedException(); + } + } + + public override long Length + { + get + { + throw new NotImplementedException(); + } + } + + public override long Position + { + get + { + throw new NotImplementedException(); + } + + set + { + throw new NotImplementedException(); + } + } + + public override void Flush() + { + throw new NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + } +} diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunLiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunLiveStream.cs deleted file mode 100644 index 7bb524c49..000000000 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunLiveStream.cs +++ /dev/null @@ -1,290 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Net; - -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun -{ - public class LegacyHdHomerunLiveStream : LiveStream, IDirectStreamProvider - { - private readonly ILogger _logger; - private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; - private readonly IServerApplicationPaths _appPaths; - private readonly IServerApplicationHost _appHost; - private readonly ISocketFactory _socketFactory; - - private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource(); - private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource(); - private readonly MulticastStream _multicastStream; - private readonly string _channelUrl; - 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) - : base(mediaSource) - { - _fileSystem = fileSystem; - _httpClient = httpClient; - _logger = logger; - _appPaths = appPaths; - _appHost = appHost; - _socketFactory = socketFactory; - _networkManager = networkManager; - OriginalStreamId = originalStreamId; - _multicastStream = new MulticastStream(_logger); - _channelUrl = channelUrl; - _numTuners = numTuners; - } - - protected override async Task OpenInternal(CancellationToken openCancellationToken) - { - _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); - - var mediaSource = OriginalMediaSource; - - var uri = new Uri(mediaSource.Path); - var localPort = _networkManager.GetRandomUnusedUdpPort(); - - _logger.Info("Opening Legacy HDHR Live stream from {0}", uri.Host); - - var taskCompletionSource = new TaskCompletionSource(); - - StartStreaming(uri.Host, localPort, taskCompletionSource, _liveStreamCancellationTokenSource.Token); - - //OpenedMediaSource.Protocol = MediaProtocol.File; - //OpenedMediaSource.Path = tempFile; - //OpenedMediaSource.ReadAtNativeFramerate = true; - - OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; - OpenedMediaSource.Protocol = MediaProtocol.Http; - OpenedMediaSource.SupportsDirectPlay = false; - OpenedMediaSource.SupportsDirectStream = true; - OpenedMediaSource.SupportsTranscoding = true; - - await taskCompletionSource.Task.ConfigureAwait(false); - - //await Task.Delay(5000).ConfigureAwait(false); - } - - public override Task Close() - { - _logger.Info("Closing Legacy HDHR live stream"); - _liveStreamCancellationTokenSource.Cancel(); - - return _liveStreamTaskCompletionSource.Task; - } - - 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); - } - - public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) - { - return _multicastStream.CopyToAsync(stream); - } - } - - // This handles the ReadAsync function only of a Stream object - // This is used to wrap a UDP socket into a stream for MulticastStream which only uses ReadAsync - public class UdpClientStream : Stream - { - private static int RtpHeaderBytes = 12; - private static int PacketSize = 1316; - private readonly ISocket _udpClient; - bool disposed; - - public UdpClientStream(ISocket udpClient) : base() - { - _udpClient = udpClient; - } - - public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException("buffer"); - - if (offset + count < 0) - throw new ArgumentOutOfRangeException("offset + count must not be negative", "offset+count"); - - if (offset + count > buffer.Length) - throw new ArgumentException("offset + count must not be greater than the length of buffer", "offset+count"); - - if (disposed) - throw new ObjectDisposedException(typeof(UdpClientStream).ToString()); - - // This will always receive a 1328 packet size (PacketSize + RtpHeaderSize) - // The RTP header will be stripped so see how many reads we need to make to fill the buffer. - int numReads = count / PacketSize; - int totalBytesRead = 0; - - for (int i = 0; i < numReads; ++i) - { - var data = await _udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); - - var bytesRead = data.ReceivedBytes - RtpHeaderBytes; - - // remove rtp header - Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead); - offset += bytesRead; - totalBytesRead += bytesRead; - } - return totalBytesRead; - } - - protected override void Dispose(bool disposing) - { - disposed = true; - } - - public override bool CanRead - { - get - { - throw new NotImplementedException(); - } - } - - public override bool CanSeek - { - get - { - throw new NotImplementedException(); - } - } - - public override bool CanWrite - { - get - { - throw new NotImplementedException(); - } - } - - public override long Length - { - get - { - throw new NotImplementedException(); - } - } - - public override long Position - { - get - { - throw new NotImplementedException(); - } - - set - { - throw new NotImplementedException(); - } - } - - public override void Flush() - { - throw new NotImplementedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotImplementedException(); - } - - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - } -} diff --git a/MediaBrowser.Model/Net/IAcceptSocket.cs b/MediaBrowser.Model/Net/IAcceptSocket.cs index 0467129c0..cac23b337 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 e4ef9f6d4..4b70f3362 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 e66950cb5..a3b0bf79a 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 77ed65aff..c7642d5d1 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 { -- cgit v1.2.3