From 9b71812325f03b8147d7ec13511974856f7daa6a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 23 Oct 2017 15:14:11 -0400 Subject: [PATCH] update live stream management --- .../HttpServer/HttpListenerHost.cs | 2 - .../LiveTv/EmbyTV/EmbyTV.cs | 24 ++- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 2 +- .../HdHomerun/HdHomerunHttpStream.cs | 55 +++--- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 34 ++-- .../HdHomerun/HdHomerunUdpStream.cs | 178 +++++++++--------- .../LiveTv/TunerHosts/LiveStream.cs | 15 +- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 4 +- SharedVersion.cs | 2 +- 9 files changed, 159 insertions(+), 157 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 1d3d4f1e5f..937eb80293 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -411,8 +411,6 @@ namespace Emby.Server.Implementations.HttpServer host = host ?? string.Empty; - _logger.Debug("Validating host {0}", host); - if (_networkManager.IsInPrivateAddressSpace(host)) { hosts.Add("localhost"); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index e7a360594e..6c254018e5 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1222,7 +1222,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _logger.Info("Closing live stream {0}", id); - await stream.Close().ConfigureAwait(false); + stream.Close(); _logger.Info("Live stream {0} closed successfully", id); } } @@ -1286,9 +1286,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV Id = timer.Id }; - if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo)) + if (!_activeRecordings.ContainsKey(timer.Id)) { - await RecordStream(timer, recordingEndDate, activeRecordingInfo, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false); + await RecordStream(timer, recordingEndDate, activeRecordingInfo).ConfigureAwait(false); } else { @@ -1397,8 +1397,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return Path.Combine(recordPath, recordingFileName); } - private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, - ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken) + private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo) { if (timer == null) { @@ -1420,19 +1419,18 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (programInfo != null) { CopyProgramInfoToTimerInfo(programInfo, timer); - //activeRecordingInfo.Program = programInfo; } string seriesPath = null; var recordPath = GetRecordingPath(timer, out seriesPath); var recordingStatus = RecordingStatus.New; + var recorder = await GetRecorder().ConfigureAwait(false); + string liveStreamId = null; try { - var recorder = await GetRecorder().ConfigureAwait(false); - var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false); _logger.Info("Opening recording stream from tuner provider"); @@ -1442,14 +1440,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var mediaStreamInfo = liveStreamInfo.Item2; liveStreamId = mediaStreamInfo.Id; - // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg - //await Task.Delay(3000, cancellationToken).ConfigureAwait(false); - recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath); recordPath = EnsureFileUnique(recordPath, timer.Id); _libraryMonitor.ReportFileSystemChangeBeginning(recordPath); - activeRecordingInfo.Path = recordPath; var duration = recordingEndDate - DateTime.UtcNow; @@ -1459,6 +1453,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV Action onStarted = async () => { + activeRecordingInfo.Path = recordPath; + + _activeRecordings.TryAdd(timer.Id, activeRecordingInfo); + timer.Status = RecordingStatus.InProgress; _timerProvider.AddOrUpdate(timer, false); @@ -1470,7 +1468,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV EnforceKeepUpTo(timer, seriesPath); }; - await recorder.Record(liveStreamInfo.Item1 as IDirectStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false); + await recorder.Record(liveStreamInfo.Item1 as IDirectStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false); recordingStatus = RecordingStatus.Completed; _logger.Info("Recording completed: {0}", recordPath); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index f8bb766d2f..59346cdec2 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -203,7 +203,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var uri = new Uri(GetApiUrl(info, false)); - using (var manager = new HdHomerunManager(_socketFactory)) + using (var manager = new HdHomerunManager(_socketFactory, Logger)) { // Legacy HdHomeruns are IPv4 only var ipInfo = _networkManager.ParseIpAddress(uri.Host); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs index f3d89c6cc9..7e0e5fc5c9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs @@ -22,8 +22,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly IHttpClient _httpClient; private readonly IServerApplicationHost _appHost; - private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource(); - public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment) : base(mediaSource, environment, fileSystem, logger, appPaths) { @@ -32,7 +30,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun OriginalStreamId = originalStreamId; } - protected override Task OpenInternal(CancellationToken openCancellationToken) + public override async Task Open(CancellationToken openCancellationToken) { LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); @@ -40,11 +38,26 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var url = mediaSource.Path; + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath)); + Logger.Info("Opening HDHR Live stream from {0}", url); - var taskCompletionSource = new TaskCompletionSource(); + var response = await _httpClient.SendAsync(new HttpRequestOptions + { + Url = url, + CancellationToken = CancellationToken.None, + BufferContent = false, + + // Increase a little bit + TimeoutMs = 30000, - StartStreaming(url, taskCompletionSource, LiveStreamCancellationTokenSource.Token); + EnableHttpCompression = false + + }, "GET").ConfigureAwait(false); + + Logger.Info("Opened HDHR stream from {0}", url); + + StartStreaming(response, LiveStreamCancellationTokenSource.Token); //OpenedMediaSource.Protocol = MediaProtocol.File; //OpenedMediaSource.Path = tempFile; @@ -58,50 +71,30 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun //OpenedMediaSource.SupportsDirectPlay = false; //OpenedMediaSource.SupportsDirectStream = true; //OpenedMediaSource.SupportsTranscoding = true; - - return taskCompletionSource.Task; - - //await Task.Delay(5000).ConfigureAwait(false); } - public override async Task Close() + public override void Close() { Logger.Info("Closing HDHR live stream"); LiveStreamCancellationTokenSource.Cancel(); - - await _liveStreamTaskCompletionSource.Task.ConfigureAwait(false); - await DeleteTempFile(TempFilePath).ConfigureAwait(false); } - private Task StartStreaming(string url, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) + private Task StartStreaming(HttpResponseInfo response, CancellationToken cancellationToken) { return Task.Run(async () => { try { - using (var response = await _httpClient.SendAsync(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = false, - - // Increase a little bit - TimeoutMs = 30000, - - EnableHttpCompression = false - - }, "GET").ConfigureAwait(false)) + using (response) { using (var stream = response.Content) { - Logger.Info("Opened HDHR stream from {0}", url); - - Logger.Info("Beginning multicastStream.CopyUntilCancelled"); + Logger.Info("Beginning HdHomerunHttpStream stream to file"); FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath)); using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None)) { - StreamHelper.CopyTo(stream, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken); + StreamHelper.CopyTo(stream, fileStream, 81920, null, cancellationToken); } } } @@ -114,7 +107,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun Logger.ErrorException("Error copying live stream.", ex); } - _liveStreamTaskCompletionSource.TrySetResult(true); + await DeleteTempFile(TempFilePath).ConfigureAwait(false); }); } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index c737c4cbae..5156f1744b 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -6,6 +6,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Net; +using MediaBrowser.Model.Logging; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { @@ -89,9 +90,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly ISocketFactory _socketFactory; private IpAddressInfo _remoteIp; - public HdHomerunManager(ISocketFactory socketFactory) + private ILogger _logger; + + public HdHomerunManager(ISocketFactory socketFactory, ILogger logger) { _socketFactory = socketFactory; + _logger = logger; } public void Dispose() @@ -140,6 +144,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun _lockkey = (uint)rand.Next(); } + var lockKeyValue = _lockkey.Value; + var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort); for (int i = 0; i < numTuners; ++i) @@ -148,7 +154,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun continue; _activeTuner = i; - var lockKeyString = String.Format("{0:d}", _lockkey.Value); + var lockKeyString = String.Format("{0:d}", lockKeyValue); var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null); await tcpClient.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false); var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); @@ -160,27 +166,27 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var commandList = commands.GetCommands(); foreach(Tuple command in commandList) { - var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, _lockkey.Value); + var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue); await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false); response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal)) { - await ReleaseLockkey(tcpClient).ConfigureAwait(false); + await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false); continue; } } var targetValue = String.Format("rtp://{0}:{1}", localIp, localPort); - var targetMsg = CreateSetMessage(i, "target", targetValue, _lockkey.Value); + var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue); await tcpClient.SendToAsync(targetMsg, 0, targetMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false); response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal)) { - await ReleaseLockkey(tcpClient).ConfigureAwait(false); + await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false); continue; } @@ -201,7 +207,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun foreach (Tuple command in commandList) { - var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey.Value); + var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey); await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false); var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked @@ -216,24 +222,28 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public async Task StopStreaming() { - if (!_lockkey.HasValue) + var lockKey = _lockkey; + + if (!lockKey.HasValue) return; using (var socket = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort)) { - await ReleaseLockkey(socket).ConfigureAwait(false); + await ReleaseLockkey(socket, lockKey.Value).ConfigureAwait(false); } } - private async Task ReleaseLockkey(ISocket tcpClient) + private async Task ReleaseLockkey(ISocket tcpClient, uint lockKeyValue) { - var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", _lockkey); + _logger.Info("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue); + + var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue); await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false); var receiveBuffer = new byte[8192]; await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false); - var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", _lockkey); + var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue); _lockkey = null; await tcpClient.SendToAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false); await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 4187fcd8fb..06326d26c6 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -1,23 +1,16 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations.IO; 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; using MediaBrowser.Model.System; -using System.Globalization; -using MediaBrowser.Controller.IO; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { @@ -26,7 +19,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly IServerApplicationHost _appHost; private readonly ISocketFactory _socketFactory; - private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource(); private readonly IHdHomerunChannelCommands _channelCommands; private readonly int _numTuners; private readonly INetworkManager _networkManager; @@ -42,7 +34,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun _numTuners = numTuners; } - protected override Task OpenInternal(CancellationToken openCancellationToken) + public override async Task Open(CancellationToken openCancellationToken) { LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); @@ -51,11 +43,53 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var uri = new Uri(mediaSource.Path); var localPort = _networkManager.GetRandomUnusedUdpPort(); + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath)); + Logger.Info("Opening HDHR UDP Live stream from {0}", uri.Host); + var remoteAddress = _networkManager.ParseIpAddress(uri.Host); + IpAddressInfo localAddress = null; + using (var tcpSocket = _socketFactory.CreateSocket(remoteAddress.AddressFamily, 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; + } + } + + var udpClient = _socketFactory.CreateUdpSocket(localPort); + var hdHomerunManager = new HdHomerunManager(_socketFactory, Logger); + + try + { + // send url to start streaming + await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + using (udpClient) + { + using (hdHomerunManager) + { + if (!(ex is OperationCanceledException)) + { + Logger.ErrorException("Error opening live stream:", ex); + } + throw; + } + } + } + var taskCompletionSource = new TaskCompletionSource(); - StartStreaming(uri.Host, localPort, taskCompletionSource, LiveStreamCancellationTokenSource.Token); + StartStreaming(udpClient, hdHomerunManager, remoteAddress, localAddress, localPort, taskCompletionSource, LiveStreamCancellationTokenSource.Token); //OpenedMediaSource.Protocol = MediaProtocol.File; //OpenedMediaSource.Path = tempFile; @@ -67,86 +101,47 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun //OpenedMediaSource.SupportsDirectStream = true; //OpenedMediaSource.SupportsTranscoding = true; - return taskCompletionSource.Task; - //await Task.Delay(5000).ConfigureAwait(false); + await taskCompletionSource.Task.ConfigureAwait(false); } - public override Task Close() + public override void Close() { Logger.Info("Closing HDHR UDP live stream"); LiveStreamCancellationTokenSource.Cancel(); - - return _liveStreamTaskCompletionSource.Task; } - private Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) + private Task StartStreaming(ISocket udpClient, HdHomerunManager hdHomerunManager, IpAddressInfo remoteAddress, IpAddressInfo localAddress, int localPort, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) { return Task.Run(async () => { - var isFirstAttempt = true; - using (var udpClient = _socketFactory.CreateUdpSocket(localPort)) + using (udpClient) { - using (var hdHomerunManager = new HdHomerunManager(_socketFactory)) + using (hdHomerunManager) { - var remoteAddress = _networkManager.ParseIpAddress(remoteIp); - IpAddressInfo localAddress = null; - using (var tcpSocket = _socketFactory.CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, false)) + try + { + await CopyTo(udpClient, TempFilePath, openTaskCompletionSource, cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException ex) { - 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; - } + Logger.Info("HDHR UDP stream cancelled or timed out from {0}", remoteAddress); + openTaskCompletionSource.TrySetException(ex); + } + catch (Exception ex) + { + Logger.ErrorException("Error opening live stream:", ex); + openTaskCompletionSource.TrySetException(ex); } - while (!cancellationToken.IsCancellationRequested) + try { - try - { - // send url to start streaming - await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, cancellationToken).ConfigureAwait(false); - - Logger.Info("Opened HDHR UDP stream from {0}", remoteAddress); - - if (!cancellationToken.IsCancellationRequested) - { - FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath)); - using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None)) - { - CopyTo(udpClient, fileStream, openTaskCompletionSource, cancellationToken); - } - } - } - catch (OperationCanceledException ex) - { - Logger.Info("HDHR UDP stream cancelled or timed out from {0}", remoteAddress); - openTaskCompletionSource.TrySetException(ex); - 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); } + catch + { - await hdHomerunManager.StopStreaming().ConfigureAwait(false); - _liveStreamTaskCompletionSource.TrySetResult(true); + } } } @@ -157,36 +152,45 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private void Resolve(TaskCompletionSource openTaskCompletionSource) { Task.Run(() => - { - openTaskCompletionSource.TrySetResult(true); - }); + { + openTaskCompletionSource.TrySetResult(true); + }); } private static int RtpHeaderBytes = 12; - private void CopyTo(ISocket udpClient, Stream target, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) + private async Task CopyTo(ISocket udpClient, string file, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) { - var source = _socketFactory.CreateNetworkStream(udpClient, false); var bufferSize = 81920; byte[] buffer = new byte[bufferSize]; int read; var resolved = false; - while ((read = source.Read(buffer, 0, buffer.Length)) != 0) + using (var source = _socketFactory.CreateNetworkStream(udpClient, false)) { - cancellationToken.ThrowIfCancellationRequested(); + using (var fileStream = FileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None)) + { + var currentCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token).Token; - read -= RtpHeaderBytes; + while ((read = await source.ReadAsync(buffer, 0, buffer.Length, currentCancellationToken).ConfigureAwait(false)) != 0) + { + cancellationToken.ThrowIfCancellationRequested(); - if (read > 0) - { - target.Write(buffer, RtpHeaderBytes, read); - } + currentCancellationToken = cancellationToken; - if (!resolved) - { - resolved = true; - Resolve(openTaskCompletionSource); + read -= RtpHeaderBytes; + + if (read > 0) + { + fileStream.Write(buffer, RtpHeaderBytes, read); + } + + if (!resolved) + { + resolved = true; + Resolve(openTaskCompletionSource); + } + } } } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index 685f794fde..12695cd8e7 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -47,19 +47,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts TempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts"); } - public Task Open(CancellationToken cancellationToken) - { - return OpenInternal(cancellationToken); - } - - protected virtual Task OpenInternal(CancellationToken cancellationToken) + public virtual Task Open(CancellationToken openCancellationToken) { return Task.FromResult(true); } - public virtual Task Close() + public virtual void Close() { - return Task.FromResult(true); } protected Stream GetInputStream(string path, bool allowAsyncFileRead) @@ -76,6 +70,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts protected async Task DeleteTempFile(string path, int retryCount = 0) { + if (retryCount == 0) + { + Logger.Info("Deleting temp file {0}", path); + } + try { FileSystem.DeleteFile(path); diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 2019259c56..523eec24ac 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -59,8 +59,8 @@ namespace MediaBrowser.Controller.LiveTv public interface ILiveStream { - Task Open(CancellationToken cancellationToken); - Task Close(); + Task Open(CancellationToken openCancellationToken); + void Close(); int ConsumerCount { get; } string OriginalStreamId { get; set; } bool EnableStreamSharing { get; set; } diff --git a/SharedVersion.cs b/SharedVersion.cs index 67b6004adc..7e6965cbc3 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.33.19")] +[assembly: AssemblyVersion("3.2.33.20")]