Переглянути джерело

update live stream management

Luke Pulverenti 7 роки тому
батько
коміт
9b71812325

+ 0 - 2
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");

+ 11 - 13
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);

+ 1 - 1
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);

+ 24 - 31
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<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
-
         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<bool>();
+            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<bool> 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);
             });
         }
 

+ 22 - 12
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<string,string> 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<string, string> 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);

+ 91 - 87
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<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
         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<bool>();
 
-            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<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+        private Task StartStreaming(ISocket udpClient, HdHomerunManager hdHomerunManager, IpAddressInfo remoteAddress, IpAddressInfo localAddress, int localPort, TaskCompletionSource<bool> 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<bool> openTaskCompletionSource)
         {
             Task.Run(() =>
-           {
-               openTaskCompletionSource.TrySetResult(true);
-           });
+            {
+                openTaskCompletionSource.TrySetResult(true);
+            });
         }
 
         private static int RtpHeaderBytes = 12;
-        private void CopyTo(ISocket udpClient, Stream target, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+        private async Task CopyTo(ISocket udpClient, string file, TaskCompletionSource<bool> 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);
+                        }
+                    }
                 }
             }
         }

+ 7 - 8
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);

+ 2 - 2
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; }

+ 1 - 1
SharedVersion.cs

@@ -1,3 +1,3 @@
 using System.Reflection;
 
-[assembly: AssemblyVersion("3.2.33.19")]
+[assembly: AssemblyVersion("3.2.33.20")]