浏览代码

Clean up livestreaming code

Bond_009 6 年之前
父节点
当前提交
237db8ae92

+ 22 - 14
Emby.Dlna/PlayTo/SsdpHttpClient.cs

@@ -16,6 +16,8 @@ namespace Emby.Dlna.PlayTo
         private const string USERAGENT = "Microsoft-Windows/6.2 UPnP/1.0 Microsoft-DLNA DLNADOC/1.50";
         private const string FriendlyName = "Jellyfin";
 
+        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
         private readonly IHttpClient _httpClient;
         private readonly IServerConfigurationManager _config;
 
@@ -25,7 +27,8 @@ namespace Emby.Dlna.PlayTo
             _config = config;
         }
 
-        public async Task<XDocument> SendCommandAsync(string baseUrl,
+        public async Task<XDocument> SendCommandAsync(
+            string baseUrl,
             DeviceService service,
             string command,
             string postData,
@@ -35,12 +38,20 @@ namespace Emby.Dlna.PlayTo
             var cancellationToken = CancellationToken.None;
 
             var url = NormalizeServiceUrl(baseUrl, service.ControlUrl);
-            using (var response = await PostSoapDataAsync(url, '\"' + service.ServiceType + '#' + command + '\"', postData, header, logRequest, cancellationToken)
+            using (var response = await PostSoapDataAsync(
+                url,
+                $"\"{service.ServiceType}#{command}\"",
+                postData,
+                header,
+                logRequest,
+                cancellationToken)
                 .ConfigureAwait(false))
             using (var stream = response.Content)
             using (var reader = new StreamReader(stream, Encoding.UTF8))
             {
-                return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
+                return XDocument.Parse(
+                    await reader.ReadToEndAsync().ConfigureAwait(false),
+                    LoadOptions.PreserveWhitespace);
             }
         }
 
@@ -58,9 +69,8 @@ namespace Emby.Dlna.PlayTo
             return baseUrl + serviceUrl;
         }
 
-        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
-        public async Task SubscribeAsync(string url,
+        public async Task SubscribeAsync(
+            string url,
             string ip,
             int port,
             string localIp,
@@ -101,14 +111,12 @@ namespace Emby.Dlna.PlayTo
             options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
 
             using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
+            using (var stream = response.Content)
+            using (var reader = new StreamReader(stream, Encoding.UTF8))
             {
-                using (var stream = response.Content)
-                {
-                    using (var reader = new StreamReader(stream, Encoding.UTF8))
-                    {
-                        return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
-                    }
-                }
+                return XDocument.Parse(
+                    await reader.ReadToEndAsync().ConfigureAwait(false),
+                    LoadOptions.PreserveWhitespace);
             }
         }
 
@@ -122,7 +130,7 @@ namespace Emby.Dlna.PlayTo
         {
             if (soapAction[0] != '\"')
             {
-                soapAction = '\"' + soapAction + '\"';
+                soapAction = $"\"{soapAction}\"";
             }
 
             var options = new HttpRequestOptions

+ 4 - 20
Emby.Server.Implementations/ApplicationHost.cs

@@ -315,8 +315,6 @@ namespace Emby.Server.Implementations
 
         private IMediaSourceManager MediaSourceManager { get; set; }
 
-        private IPlaylistManager PlaylistManager { get; set; }
-
         private readonly IConfiguration _configuration;
 
         /// <summary>
@@ -325,14 +323,6 @@ namespace Emby.Server.Implementations
         /// <value>The installation manager.</value>
         protected IInstallationManager InstallationManager { get; private set; }
 
-        /// <summary>
-        /// Gets or sets the zip client.
-        /// </summary>
-        /// <value>The zip client.</value>
-        protected IZipClient ZipClient { get; private set; }
-
-        protected IHttpResultFactory HttpResultFactory { get; private set; }
-
         protected IAuthService AuthService { get; private set; }
 
         public IStartupOptions StartupOptions { get; }
@@ -680,8 +670,6 @@ namespace Emby.Server.Implementations
             await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
         }
 
-        public static IStreamHelper StreamHelper { get; set; }
-
         /// <summary>
         /// Registers resources that classes will depend on
         /// </summary>
@@ -725,8 +713,7 @@ namespace Emby.Server.Implementations
             ProcessFactory = new ProcessFactory();
             serviceCollection.AddSingleton(ProcessFactory);
 
-            ApplicationHost.StreamHelper = new StreamHelper();
-            serviceCollection.AddSingleton(StreamHelper);
+            serviceCollection.AddSingleton(typeof(IStreamHelper), typeof(StreamHelper));
 
             serviceCollection.AddSingleton(typeof(ICryptoProvider), typeof(CryptographyProvider));
 
@@ -735,11 +722,9 @@ namespace Emby.Server.Implementations
 
             serviceCollection.AddSingleton(typeof(IInstallationManager), typeof(InstallationManager));
 
-            ZipClient = new ZipClient();
-            serviceCollection.AddSingleton(ZipClient);
+            serviceCollection.AddSingleton(typeof(IZipClient), typeof(ZipClient));
 
-            HttpResultFactory = new HttpResultFactory(LoggerFactory, FileSystemManager, JsonSerializer, StreamHelper);
-            serviceCollection.AddSingleton(HttpResultFactory);
+            serviceCollection.AddSingleton(typeof(IHttpResultFactory), typeof(HttpResultFactory));
 
             serviceCollection.AddSingleton<IServerApplicationHost>(this);
             serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
@@ -836,8 +821,7 @@ namespace Emby.Server.Implementations
             CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager);
             serviceCollection.AddSingleton(CollectionManager);
 
-            PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LoggerFactory, UserManager, ProviderManager);
-            serviceCollection.AddSingleton(PlaylistManager);
+            serviceCollection.AddSingleton(typeof(IPlaylistManager), typeof(PlaylistManager));
 
             LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager);
             serviceCollection.AddSingleton(LiveTvManager);

+ 96 - 88
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -4,6 +4,7 @@ using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Net;
+using System.Net.Http;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
@@ -31,6 +32,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         private readonly IServerApplicationHost _appHost;
         private readonly ISocketFactory _socketFactory;
         private readonly INetworkManager _networkManager;
+        private readonly IStreamHelper _streamHelper;
 
         public HdHomerunHost(
             IServerConfigurationManager config,
@@ -40,29 +42,25 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             IHttpClient httpClient,
             IServerApplicationHost appHost,
             ISocketFactory socketFactory,
-            INetworkManager networkManager)
+            INetworkManager networkManager,
+            IStreamHelper streamHelper)
             : base(config, logger, jsonSerializer, fileSystem)
         {
             _httpClient = httpClient;
             _appHost = appHost;
             _socketFactory = socketFactory;
             _networkManager = networkManager;
+            _streamHelper = streamHelper;
         }
 
         public string Name => "HD Homerun";
 
-        public override string Type => DeviceType;
-
-        public static string DeviceType => "hdhomerun";
+        public override string Type => "hdhomerun";
 
         protected override string ChannelIdPrefix => "hdhr_";
 
         private string GetChannelId(TunerHostInfo info, Channels i)
-        {
-            var id = ChannelIdPrefix + i.GuideNumber;
-
-            return id;
-        }
+            => ChannelIdPrefix + i.GuideNumber;
 
         private async Task<List<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
         {
@@ -74,19 +72,18 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 CancellationToken = cancellationToken,
                 BufferContent = false
             };
-            using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
-            {
-                using (var stream = response.Content)
-                {
-                    var lineup = await JsonSerializer.DeserializeFromStreamAsync<List<Channels>>(stream).ConfigureAwait(false) ?? new List<Channels>();
 
-                    if (info.ImportFavoritesOnly)
-                    {
-                        lineup = lineup.Where(i => i.Favorite).ToList();
-                    }
+            using (var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false))
+            using (var stream = response.Content)
+            {
+                var lineup = await JsonSerializer.DeserializeFromStreamAsync<List<Channels>>(stream).ConfigureAwait(false) ?? new List<Channels>();
 
-                    return lineup.Where(i => !i.DRM).ToList();
+                if (info.ImportFavoritesOnly)
+                {
+                    lineup = lineup.Where(i => i.Favorite).ToList();
                 }
+
+                return lineup.Where(i => !i.DRM).ToList();
             }
         }
 
@@ -139,23 +136,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     Url = string.Format("{0}/discover.json", GetApiUrl(info)),
                     CancellationToken = cancellationToken,
                     BufferContent = false
-
-                }, "GET").ConfigureAwait(false))
+                }, HttpMethod.Get).ConfigureAwait(false))
+                using (var stream = response.Content)
                 {
-                    using (var stream = response.Content)
-                    {
-                        var discoverResponse = await JsonSerializer.DeserializeFromStreamAsync<DiscoverResponse>(stream).ConfigureAwait(false);
+                    var discoverResponse = await JsonSerializer.DeserializeFromStreamAsync<DiscoverResponse>(stream).ConfigureAwait(false);
 
-                        if (!string.IsNullOrEmpty(cacheKey))
+                    if (!string.IsNullOrEmpty(cacheKey))
+                    {
+                        lock (_modelCache)
                         {
-                            lock (_modelCache)
-                            {
-                                _modelCache[cacheKey] = discoverResponse;
-                            }
+                            _modelCache[cacheKey] = discoverResponse;
                         }
-
-                        return discoverResponse;
                     }
+
+                    return discoverResponse;
                 }
             }
             catch (HttpException ex)
@@ -186,36 +180,36 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         {
             var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
 
-            using (var stream = await _httpClient.Get(new HttpRequestOptions()
+            using (var response = await _httpClient.SendAsync(new HttpRequestOptions()
             {
                 Url = string.Format("{0}/tuners.html", GetApiUrl(info)),
                 CancellationToken = cancellationToken,
                 BufferContent = false
-            }))
+            }, HttpMethod.Get))
+            using (var stream = response.Content)
+            using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8))
             {
                 var tuners = new List<LiveTvTunerInfo>();
-                using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8))
+                while (!sr.EndOfStream)
                 {
-                    while (!sr.EndOfStream)
+                    string line = StripXML(sr.ReadLine());
+                    if (line.Contains("Channel"))
                     {
-                        string line = StripXML(sr.ReadLine());
-                        if (line.Contains("Channel"))
+                        LiveTvTunerStatus status;
+                        var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
+                        var name = line.Substring(0, index - 1);
+                        var currentChannel = line.Substring(index + 7);
+                        if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; }
+                        tuners.Add(new LiveTvTunerInfo
                         {
-                            LiveTvTunerStatus status;
-                            var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
-                            var name = line.Substring(0, index - 1);
-                            var currentChannel = line.Substring(index + 7);
-                            if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; }
-                            tuners.Add(new LiveTvTunerInfo
-                            {
-                                Name = name,
-                                SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber,
-                                ProgramName = currentChannel,
-                                Status = status
-                            });
-                        }
+                            Name = name,
+                            SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber,
+                            ProgramName = currentChannel,
+                            Status = status
+                        });
                     }
                 }
+
                 return tuners;
             }
         }
@@ -245,6 +239,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     bufferIndex++;
                 }
             }
+
             return new string(buffer, 0, bufferIndex);
         }
 
@@ -256,7 +251,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             var uri = new Uri(GetApiUrl(info));
 
-            using (var manager = new HdHomerunManager(Logger))
+            using (var manager = new HdHomerunManager())
             {
                 // Legacy HdHomeruns are IPv4 only
                 var ipInfo = IPAddress.Parse(uri.Host);
@@ -276,6 +271,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     });
                 }
             }
+
             return tuners;
         }
 
@@ -434,12 +430,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             {
                 videoCodec = channelInfo.VideoCodec;
             }
+
             string audioCodec = channelInfo.AudioCodec;
 
             if (!videoBitrate.HasValue)
             {
                 videoBitrate = isHd ? 15000000 : 2000000;
             }
+
             int? audioBitrate = isHd ? 448000 : 192000;
 
             // normalize
@@ -461,6 +459,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             {
                 id = "native";
             }
+
             id += "_" + channelId.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_" + url.GetMD5().ToString("N", CultureInfo.InvariantCulture);
 
             var mediaSource = new MediaSourceInfo
@@ -527,29 +526,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             }
             else
             {
-                try
-                {
-                    var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
+                var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
 
-                    if (modelInfo != null && modelInfo.SupportsTranscoding)
+                if (modelInfo != null && modelInfo.SupportsTranscoding)
+                {
+                    if (info.AllowHWTranscoding)
                     {
-                        if (info.AllowHWTranscoding)
-                        {
-                            list.Add(GetMediaSource(info, hdhrId, channelInfo, "heavy"));
-
-                            list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet540"));
-                            list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet480"));
-                            list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet360"));
-                            list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet240"));
-                            list.Add(GetMediaSource(info, hdhrId, channelInfo, "mobile"));
-                        }
+                        list.Add(GetMediaSource(info, hdhrId, channelInfo, "heavy"));
 
-                        list.Add(GetMediaSource(info, hdhrId, channelInfo, "native"));
+                        list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet540"));
+                        list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet480"));
+                        list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet360"));
+                        list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet240"));
+                        list.Add(GetMediaSource(info, hdhrId, channelInfo, "mobile"));
                     }
-                }
-                catch
-                {
 
+                    list.Add(GetMediaSource(info, hdhrId, channelInfo, "native"));
                 }
 
                 if (list.Count == 0)
@@ -582,7 +574,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner)
             {
-                return new HdHomerunUdpStream(mediaSource, info, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Path), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
+                return new HdHomerunUdpStream(
+                    mediaSource,
+                    info,
+                    streamId,
+                    new LegacyHdHomerunChannelCommands(hdhomerunChannel.Path),
+                    modelInfo.TunerCount,
+                    FileSystem,
+                    Logger,
+                    Config.ApplicationPaths,
+                    _appHost,
+                    _socketFactory,
+                    _networkManager,
+                    _streamHelper);
             }
 
             var enableHttpStream = true;
@@ -599,10 +603,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 }
                 mediaSource.Path = httpUrl;
 
-                return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
-            }
-
-            return new HdHomerunUdpStream(mediaSource, info, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
+                return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _streamHelper);
+            }
+
+            return new HdHomerunUdpStream(
+                mediaSource,
+                info,
+                streamId,
+                new HdHomerunChannelCommands(hdhomerunChannel.Number, profile),
+                modelInfo.TunerCount,
+                FileSystem,
+                Logger,
+                Config.ApplicationPaths,
+                _appHost,
+                _socketFactory,
+                _networkManager,
+                _streamHelper);
         }
 
         public async Task Validate(TunerHostInfo info)
@@ -701,9 +717,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 catch (OperationCanceledException)
                 {
                 }
-                catch
+                catch (Exception ex)
                 {
                     // Socket timeout indicates all messages have been received.
+                    Logger.LogError(ex, "Error while sending discovery message");
                 }
             }
 
@@ -718,21 +735,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 Url = url
             };
 
-            try
-            {
-                var modelInfo = await GetModelInfo(hostInfo, false, cancellationToken).ConfigureAwait(false);
-
-                hostInfo.DeviceId = modelInfo.DeviceID;
-                hostInfo.FriendlyName = modelInfo.FriendlyName;
+            var modelInfo = await GetModelInfo(hostInfo, false, cancellationToken).ConfigureAwait(false);
 
-                return hostInfo;
-            }
-            catch
-            {
-                // logged at lower levels
-            }
+            hostInfo.DeviceId = modelInfo.DeviceID;
+            hostInfo.FriendlyName = modelInfo.FriendlyName;
 
-            return null;
+            return hostInfo;
         }
     }
 }

+ 56 - 59
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs

@@ -14,7 +14,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 {
     public interface IHdHomerunChannelCommands
     {
-        IEnumerable<Tuple<string, string>> GetCommands();
+        IEnumerable<(string, string)> GetCommands();
     }
 
     public class LegacyHdHomerunChannelCommands : IHdHomerunChannelCommands
@@ -33,16 +33,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             }
         }
 
-        public IEnumerable<Tuple<string, string>> GetCommands()
+        public IEnumerable<(string, string)> GetCommands()
         {
-            var commands = new List<Tuple<string, string>>();
-
             if (!string.IsNullOrEmpty(_channel))
-                commands.Add(Tuple.Create("channel", _channel));
+            {
+                yield return ("channel", _channel);
+            }
 
             if (!string.IsNullOrEmpty(_program))
-                commands.Add(Tuple.Create("program", _program));
-            return commands;
+            {
+                yield return ("program", _program);
+            }
         }
     }
 
@@ -57,23 +58,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             _profile = profile;
         }
 
-        public IEnumerable<Tuple<string, string>> GetCommands()
+        public IEnumerable<(string, string)> GetCommands()
         {
-            var commands = new List<Tuple<string, string>>();
-
             if (!string.IsNullOrEmpty(_channel))
             {
-                if (!string.IsNullOrEmpty(_profile) && !string.Equals(_profile, "native", StringComparison.OrdinalIgnoreCase))
+                if (!string.IsNullOrEmpty(_profile)
+                    && !string.Equals(_profile, "native", StringComparison.OrdinalIgnoreCase))
                 {
-                    commands.Add(Tuple.Create("vchannel", string.Format("{0} transcode={1}", _channel, _profile)));
+                    yield return ("vchannel", $"{_channel} transcode={_profile}");
                 }
                 else
                 {
-                    commands.Add(Tuple.Create("vchannel", _channel));
+                    yield return ("vchannel", _channel);
                 }
             }
-
-            return commands;
         }
     }
 
@@ -87,19 +85,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         private const ushort GetSetRequest = 4;
         private const ushort GetSetReply = 5;
 
-        private readonly ILogger _logger;
-
         private uint? _lockkey = null;
         private int _activeTuner = -1;
         private IPEndPoint _remoteEndPoint;
 
         private TcpClient _tcpClient;
 
-        public HdHomerunManager(ILogger logger)
-        {
-            _logger = logger;
-        }
-
         public void Dispose()
         {
             using (var socket = _tcpClient)
@@ -108,8 +99,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 {
                     _tcpClient = null;
 
-                    var task = StopStreaming(socket);
-                    Task.WaitAll(task);
+                    StopStreaming(socket).GetAwaiter().GetResult();
                 }
             }
         }
@@ -174,19 +164,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false);
                     int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
                     // parse response to make sure it worked
-                    if (!ParseReturnMessage(buffer, receivedBytes, out var returnVal))
+                    if (!ParseReturnMessage(buffer, receivedBytes, out _))
                     {
                         continue;
                     }
 
                     var commandList = commands.GetCommands();
-                    foreach (Tuple<string, string> command in commandList)
+                    foreach (var command in commandList)
                     {
                         var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue);
                         await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false);
                         receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
                         // parse response to make sure it worked
-                        if (!ParseReturnMessage(buffer, receivedBytes, out returnVal))
+                        if (!ParseReturnMessage(buffer, receivedBytes, out _))
                         {
                             await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false);
                             continue;
@@ -199,7 +189,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     await stream.WriteAsync(targetMsg, 0, targetMsg.Length, cancellationToken).ConfigureAwait(false);
                     receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
                     // parse response to make sure it worked
-                    if (!ParseReturnMessage(buffer, receivedBytes, out returnVal))
+                    if (!ParseReturnMessage(buffer, receivedBytes, out _))
                     {
                         await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false);
                         continue;
@@ -231,13 +221,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 byte[] buffer = ArrayPool<byte>.Shared.Rent(8192);
                 try
                 {
-                    foreach (Tuple<string, string> command in commandList)
+                    foreach (var command in commandList)
                     {
                         var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey);
                         await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false);
                         int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
                         // parse response to make sure it worked
-                        if (!ParseReturnMessage(buffer, receivedBytes, out string returnVal))
+                        if (!ParseReturnMessage(buffer, receivedBytes, out _))
                         {
                             return;
                         }
@@ -264,21 +254,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
         private async Task ReleaseLockkey(TcpClient client, uint lockKeyValue)
         {
-            _logger.LogInformation("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue);
-
             var stream = client.GetStream();
 
             var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
-            await stream.WriteAsync(releaseTarget, 0, releaseTarget.Length, CancellationToken.None).ConfigureAwait(false);
+            await stream.WriteAsync(releaseTarget, 0, releaseTarget.Length).ConfigureAwait(false);
 
             var buffer = ArrayPool<byte>.Shared.Rent(8192);
             try
             {
-                await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false);
+                await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
                 var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue);
                 _lockkey = null;
-                await stream.WriteAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, CancellationToken.None).ConfigureAwait(false);
-                await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false);
+                await stream.WriteAsync(releaseKeyMsg, 0, releaseKeyMsg.Length).ConfigureAwait(false);
+                await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
             }
             finally
             {
@@ -316,7 +304,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             int messageLength = byteName.Length + byteValue.Length + 12;
             if (lockkey.HasValue)
+            {
                 messageLength += 6;
+            }
 
             var message = new byte[messageLength];
 
@@ -324,21 +314,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             bool flipEndian = BitConverter.IsLittleEndian;
 
-            message[offset] = GetSetValue;
-            offset++;
-            message[offset] = Convert.ToByte(byteValue.Length);
-            offset++;
+            message[offset++] = GetSetValue;
+            message[offset++] = Convert.ToByte(byteValue.Length);
             Buffer.BlockCopy(byteValue, 0, message, offset, byteValue.Length);
             offset += byteValue.Length;
             if (lockkey.HasValue)
             {
-                message[offset] = GetSetLockkey;
-                offset++;
-                message[offset] = (byte)4;
-                offset++;
+                message[offset++] = GetSetLockkey;
+                message[offset++] = (byte)4;
                 var lockKeyBytes = BitConverter.GetBytes(lockkey.Value);
                 if (flipEndian)
+                {
                     Array.Reverse(lockKeyBytes);
+                }
+
                 Buffer.BlockCopy(lockKeyBytes, 0, message, offset, 4);
                 offset += 4;
             }
@@ -346,7 +335,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             // calculate crc and insert at the end of the message
             var crcBytes = BitConverter.GetBytes(HdHomerunCrc.GetCrc32(message, messageLength - 4));
             if (flipEndian)
+            {
                 Array.Reverse(crcBytes);
+            }
+
             Buffer.BlockCopy(crcBytes, 0, message, offset, 4);
 
             return message;
@@ -375,10 +367,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             offset += 2;
 
             // insert tag name and length
-            message[offset] = GetSetName;
-            offset++;
-            message[offset] = Convert.ToByte(byteName.Length);
-            offset++;
+            message[offset++] = GetSetName;
+            message[offset++] = Convert.ToByte(byteName.Length);
 
             // insert name string
             Buffer.BlockCopy(byteName, 0, message, offset, byteName.Length);
@@ -392,7 +382,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             returnVal = string.Empty;
 
             if (numBytes < 4)
+            {
                 return false;
+            }
 
             var flipEndian = BitConverter.IsLittleEndian;
             int offset = 0;
@@ -400,45 +392,49 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             Buffer.BlockCopy(buf, offset, msgTypeBytes, 0, msgTypeBytes.Length);
 
             if (flipEndian)
+            {
                 Array.Reverse(msgTypeBytes);
+            }
 
             var msgType = BitConverter.ToUInt16(msgTypeBytes, 0);
             offset += 2;
 
             if (msgType != GetSetReply)
+            {
                 return false;
+            }
 
             byte[] msgLengthBytes = new byte[2];
             Buffer.BlockCopy(buf, offset, msgLengthBytes, 0, msgLengthBytes.Length);
             if (flipEndian)
+            {
                 Array.Reverse(msgLengthBytes);
+            }
 
             var msgLength = BitConverter.ToUInt16(msgLengthBytes, 0);
             offset += 2;
 
             if (numBytes < msgLength + 8)
+            {
                 return false;
+            }
 
-            var nameTag = buf[offset];
-            offset++;
+            var nameTag = buf[offset++];
 
-            var nameLength = buf[offset];
-            offset++;
+            var nameLength = buf[offset++];
 
             // skip the name field to get to value for return
             offset += nameLength;
 
-            var valueTag = buf[offset];
-            offset++;
+            var valueTag = buf[offset++];
 
-            var valueLength = buf[offset];
-            offset++;
+            var valueLength = buf[offset++];
 
             returnVal = Encoding.UTF8.GetString(buf, offset, valueLength - 1); // remove null terminator
             return true;
         }
 
-        private class HdHomerunCrc
+        private static class HdHomerunCrc
         {
             private static uint[] crc_table = {
             0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
@@ -510,15 +506,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             {
                 var hash = 0xffffffff;
                 for (var i = 0; i < numBytes; i++)
+                {
                     hash = (hash >> 8) ^ crc_table[(hash ^ bytes[i]) & 0xff];
+                }
 
                 var tmp = ~hash & 0xffffffff;
                 var b0 = tmp & 0xff;
                 var b1 = (tmp >> 8) & 0xff;
                 var b2 = (tmp >> 16) & 0xff;
                 var b3 = (tmp >> 24) & 0xff;
-                hash = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
-                return hash;
+                return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
             }
         }
     }

+ 39 - 38
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.IO;
 using System.Net;
@@ -18,6 +19,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 {
     public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider
     {
+        private const int RtpHeaderBytes = 12;
+
         private readonly IServerApplicationHost _appHost;
         private readonly MediaBrowser.Model.Net.ISocketFactory _socketFactory;
 
@@ -32,13 +35,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             IHdHomerunChannelCommands channelCommands,
             int numTuners,
             IFileSystem fileSystem,
-            IHttpClient httpClient,
             ILogger logger,
             IServerApplicationPaths appPaths,
             IServerApplicationHost appHost,
             MediaBrowser.Model.Net.ISocketFactory socketFactory,
-            INetworkManager networkManager)
-            : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths)
+            INetworkManager networkManager,
+            IStreamHelper streamHelper)
+            : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths, streamHelper)
         {
             _appHost = appHost;
             _socketFactory = socketFactory;
@@ -80,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             }
 
             var udpClient = _socketFactory.CreateUdpSocket(localPort);
-            var hdHomerunManager = new HdHomerunManager(Logger);
+            var hdHomerunManager = new HdHomerunManager();
 
             try
             {
@@ -103,7 +106,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             var taskCompletionSource = new TaskCompletionSource<bool>();
 
-            await StartStreaming(udpClient, hdHomerunManager, remoteAddress, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
+            await StartStreaming(
+                udpClient,
+                hdHomerunManager,
+                remoteAddress,
+                taskCompletionSource,
+                LiveStreamCancellationTokenSource.Token).ConfigureAwait(false);
 
             //OpenedMediaSource.Protocol = MediaProtocol.File;
             //OpenedMediaSource.Path = tempFile;
@@ -148,50 +156,43 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             });
         }
 
-        private static void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
-        {
-            Task.Run(() =>
-            {
-                openTaskCompletionSource.TrySetResult(true);
-            });
-        }
-
-        private const int RtpHeaderBytes = 12;
-
         private async Task CopyTo(MediaBrowser.Model.Net.ISocket udpClient, string file, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
         {
-            var bufferSize = 81920;
-
-            byte[] buffer = new byte[bufferSize];
-            int read;
-            var resolved = false;
-
-            using (var source = _socketFactory.CreateNetworkStream(udpClient, false))
-            using (var fileStream = FileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
+            byte[] buffer = ArrayPool<byte>.Shared.Rent(StreamDefaults.DefaultCopyToBufferSize);
+            try
             {
-                var currentCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token).Token;
-
-                while ((read = await source.ReadAsync(buffer, 0, buffer.Length, currentCancellationToken).ConfigureAwait(false)) != 0)
+                using (var source = _socketFactory.CreateNetworkStream(udpClient, false))
+                using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
                 {
-                    cancellationToken.ThrowIfCancellationRequested();
+                    var currentCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token).Token;
+                    int read;
+                    var resolved = false;
+                    while ((read = await source.ReadAsync(buffer, 0, buffer.Length, currentCancellationToken).ConfigureAwait(false)) != 0)
+                    {
+                        cancellationToken.ThrowIfCancellationRequested();
 
-                    currentCancellationToken = cancellationToken;
+                        currentCancellationToken = cancellationToken;
 
-                    read -= RtpHeaderBytes;
+                        read -= RtpHeaderBytes;
 
-                    if (read > 0)
-                    {
-                        fileStream.Write(buffer, RtpHeaderBytes, read);
-                    }
+                        if (read > 0)
+                        {
+                            await fileStream.WriteAsync(buffer, RtpHeaderBytes, read).ConfigureAwait(false);
+                        }
 
-                    if (!resolved)
-                    {
-                        resolved = true;
-                        DateOpened = DateTime.UtcNow;
-                        Resolve(openTaskCompletionSource);
+                        if (!resolved)
+                        {
+                            resolved = true;
+                            DateOpened = DateTime.UtcNow;
+                            openTaskCompletionSource.TrySetResult(true);
+                        }
                     }
                 }
             }
+            finally
+            {
+                ArrayPool<byte>.Shared.Return(buffer);
+            }
         }
     }
 }

+ 44 - 34
Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs

@@ -16,27 +16,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 {
     public class LiveStream : ILiveStream
     {
-        public MediaSourceInfo OriginalMediaSource { get; set; }
-        public MediaSourceInfo MediaSource { get; set; }
-
-        public int ConsumerCount { get; set; }
-
-        public string OriginalStreamId { get; set; }
-        public bool EnableStreamSharing { get; set; }
-        public string UniqueId { get; }
-
         protected readonly IFileSystem FileSystem;
         protected readonly IServerApplicationPaths AppPaths;
+        protected readonly IStreamHelper StreamHelper;
 
         protected string TempFilePath;
         protected readonly ILogger Logger;
         protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource();
 
-        public string TunerHostId { get; }
-
-        public DateTime DateOpened { get; protected set; }
-
-        public LiveStream(MediaSourceInfo mediaSource, TunerHostInfo tuner, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths)
+        public LiveStream(
+            MediaSourceInfo mediaSource,
+            TunerHostInfo tuner,
+            IFileSystem fileSystem,
+            ILogger logger,
+            IServerApplicationPaths appPaths,
+            IStreamHelper streamHelper)
         {
             OriginalMediaSource = mediaSource;
             FileSystem = fileSystem;
@@ -51,11 +45,27 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             }
 
             AppPaths = appPaths;
+            StreamHelper = streamHelper;
 
             ConsumerCount = 1;
             SetTempFilePath("ts");
         }
 
+        protected virtual int EmptyReadLimit => 1000;
+
+        public MediaSourceInfo OriginalMediaSource { get; set; }
+        public MediaSourceInfo MediaSource { get; set; }
+
+        public int ConsumerCount { get; set; }
+
+        public string OriginalStreamId { get; set; }
+        public bool EnableStreamSharing { get; set; }
+        public string UniqueId { get; }
+
+        public string TunerHostId { get; }
+
+        public DateTime DateOpened { get; protected set; }
+
         protected void SetTempFilePath(string extension)
         {
             TempFilePath = Path.Combine(AppPaths.GetTranscodingTempPath(), UniqueId + "." + extension);
@@ -71,24 +81,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         {
             EnableStreamSharing = false;
 
-            Logger.LogInformation("Closing " + GetType().Name);
+            Logger.LogInformation("Closing {Type}", GetType().Name);
 
             LiveStreamCancellationTokenSource.Cancel();
 
             return Task.CompletedTask;
         }
 
-        protected Stream GetInputStream(string path, bool allowAsyncFileRead)
-        {
-            var fileOpenOptions = FileOpenOptions.SequentialScan;
-
-            if (allowAsyncFileRead)
-            {
-                fileOpenOptions |= FileOpenOptions.Asynchronous;
-            }
-
-            return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions);
-        }
+        protected FileStream GetInputStream(string path, bool allowAsyncFileRead)
+            => new FileStream(
+                path,
+                FileMode.Open,
+                FileAccess.Read,
+                FileShare.ReadWrite,
+                StreamDefaults.DefaultFileStreamBufferSize,
+                allowAsyncFileRead ? FileOptions.SequentialScan | FileOptions.Asynchronous : FileOptions.SequentialScan);
 
         public Task DeleteTempFiles()
         {
@@ -144,8 +151,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10;
 
             var nextFileInfo = GetNextFile(null);
-            var nextFile = nextFileInfo.Item1;
-            var isLastFile = nextFileInfo.Item2;
+            var nextFile = nextFileInfo.file;
+            var isLastFile = nextFileInfo.isLastFile;
 
             while (!string.IsNullOrEmpty(nextFile))
             {
@@ -155,8 +162,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
                 seekFile = false;
                 nextFileInfo = GetNextFile(nextFile);
-                nextFile = nextFileInfo.Item1;
-                isLastFile = nextFileInfo.Item2;
+                nextFile = nextFileInfo.file;
+                isLastFile = nextFileInfo.isLastFile;
             }
 
             Logger.LogInformation("Live Stream ended.");
@@ -180,19 +187,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
         private async Task CopyFile(string path, bool seekFile, int emptyReadLimit, bool allowAsync, Stream stream, CancellationToken cancellationToken)
         {
-            using (var inputStream = (FileStream)GetInputStream(path, allowAsync))
+            using (var inputStream = GetInputStream(path, allowAsync))
             {
                 if (seekFile)
                 {
                     TrySeek(inputStream, -20000);
                 }
 
-                await ApplicationHost.StreamHelper.CopyToAsync(inputStream, stream, 81920, emptyReadLimit, cancellationToken).ConfigureAwait(false);
+                await StreamHelper.CopyToAsync(
+                    inputStream,
+                    stream,
+                    StreamDefaults.DefaultCopyToBufferSize,
+                    emptyReadLimit,
+                    cancellationToken).ConfigureAwait(false);
             }
         }
 
-        protected virtual int EmptyReadLimit => 1000;
-
         private void TrySeek(FileStream stream, long offset)
         {
             if (!stream.CanSeek)

+ 15 - 4
Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -28,14 +28,25 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         private readonly IServerApplicationHost _appHost;
         private readonly INetworkManager _networkManager;
         private readonly IMediaSourceManager _mediaSourceManager;
-
-        public M3UTunerHost(IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, INetworkManager networkManager)
+        private readonly IStreamHelper _streamHelper;
+
+        public M3UTunerHost(
+            IServerConfigurationManager config,
+            IMediaSourceManager mediaSourceManager,
+            ILogger logger,
+            IJsonSerializer jsonSerializer,
+            IFileSystem fileSystem,
+            IHttpClient httpClient,
+            IServerApplicationHost appHost,
+            INetworkManager networkManager,
+            IStreamHelper streamHelper)
             : base(config, logger, jsonSerializer, fileSystem)
         {
             _httpClient = httpClient;
             _appHost = appHost;
             _networkManager = networkManager;
             _mediaSourceManager = mediaSourceManager;
+            _streamHelper = streamHelper;
         }
 
         public override string Type => "m3u";
@@ -103,11 +114,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
                 if (!_disallowedSharedStreamExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
                 {
-                    return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
+                    return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _streamHelper);
                 }
             }
 
-            return new LiveStream(mediaSource, info, FileSystem, Logger, Config.ApplicationPaths);
+            return new LiveStream(mediaSource, info, FileSystem, Logger, Config.ApplicationPaths, _streamHelper);
         }
 
         public async Task Validate(TunerHostInfo info)

+ 18 - 3
Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs

@@ -19,8 +19,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         private readonly IHttpClient _httpClient;
         private readonly IServerApplicationHost _appHost;
 
-        public SharedHttpStream(MediaSourceInfo mediaSource, TunerHostInfo tunerHostInfo, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost)
-            : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths)
+        public SharedHttpStream(
+            MediaSourceInfo mediaSource,
+            TunerHostInfo tunerHostInfo,
+            string originalStreamId,
+            IFileSystem fileSystem,
+            IHttpClient httpClient,
+            ILogger logger,
+            IServerApplicationPaths appPaths,
+            IServerApplicationHost appHost,
+            IStreamHelper streamHelper)
+            : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths, streamHelper)
         {
             _httpClient = httpClient;
             _appHost = appHost;
@@ -118,7 +127,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                     using (var stream = response.Content)
                     using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
                     {
-                        await ApplicationHost.StreamHelper.CopyToAsync(stream, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken).ConfigureAwait(false);
+                        await StreamHelper.CopyToAsync(
+                            stream,
+                            fileStream,
+                            StreamDefaults.DefaultFileStreamBufferSize,
+                            () => Resolve(openTaskCompletionSource),
+                            cancellationToken).ConfigureAwait(false);
                     }
                 }
                 catch (OperationCanceledException)
@@ -128,6 +142,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                 {
                     Logger.LogError(ex, "Error copying live stream.");
                 }
+
                 EnableStreamSharing = false;
                 await DeleteTempFiles(new List<string> { TempFilePath }).ConfigureAwait(false);
             });

+ 1 - 1
MediaBrowser.Model/IO/StreamDefaults.cs

@@ -13,6 +13,6 @@ namespace MediaBrowser.Model.IO
         /// <summary>
         /// The default file stream buffer size
         /// </summary>
-        public const int DefaultFileStreamBufferSize = 81920;
+        public const int DefaultFileStreamBufferSize = 4096;
     }
 }