diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index d811152c2..41ba1967a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -117,8 +117,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts foreach (var host in hostsWithChannel) { + var resourcePool = GetLock(host.Url); + Logger.Debug("GetChannelStreamMediaSources - Waiting on tuner resource pool"); + + await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + Logger.Debug("GetChannelStreamMediaSources - Unlocked resource pool"); + try { + // Check to make sure the tuner is available + // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error + if (hostsWithChannel.Count > 1 && + !await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false)) + { + Logger.Error("Tuner is not currently available"); + continue; + } + var mediaSources = await GetChannelStreamMediaSources(host, channelId, cancellationToken).ConfigureAwait(false); // Prefix the id with the host Id so that we can easily find it @@ -133,6 +148,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { Logger.Error("Error opening tuner", ex); } + finally + { + resourcePool.Release(); + } } } @@ -170,17 +189,35 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts foreach (var host in hostsWithChannel) { + var resourcePool = GetLock(host.Url); + Logger.Debug("GetChannelStream - Waiting on tuner resource pool"); + await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + Logger.Debug("GetChannelStream - Unlocked resource pool"); try { - var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); - var resourcePool = GetLock(host.Url); + // Check to make sure the tuner is available + // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error + // If a streamId is specified then availibility has already been checked in GetChannelStreamMediaSources + if (string.IsNullOrWhiteSpace(streamId) && hostsWithChannel.Count > 1) + { + if (!await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false)) + { + Logger.Error("Tuner is not currently available"); + resourcePool.Release(); + continue; + } + } - await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false); + var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); + + //await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false); return new Tuple(stream, resourcePool); } catch (Exception ex) { Logger.Error("Error opening tuner", ex); + + resourcePool.Release(); } } } @@ -188,6 +225,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts throw new LiveTvConflictException(); } + protected async Task IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) + { + try + { + return await IsAvailableInternal(tuner, channelId, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.ErrorException("Error checking tuner availability", ex); + return false; + } + } + + protected abstract Task IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken); + /// /// The _semaphoreLocks /// @@ -202,25 +254,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts return _semaphoreLocks.GetOrAdd(url, key => new SemaphoreSlim(1, 1)); } - private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, SemaphoreSlim resourcePool, CancellationToken cancellationToken) - { - await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - await AddMediaInfoInternal(mediaSource, isAudio, cancellationToken).ConfigureAwait(false); - - // Leave the resource locked. it will be released upstream - } - catch (Exception) - { - // Release the resource if there's some kind of failure. - resourcePool.Release(); - - throw; - } - } - private async Task AddMediaInfoInternal(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) { var originalRuntime = mediaSource.RunTimeTicks; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index eebb381af..a237bb226 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun @@ -23,7 +24,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun { private readonly IHttpClient _httpClient; - public HdHomerunHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient) : base(config, logger, jsonSerializer, mediaEncoder) + public HdHomerunHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient) + : base(config, logger, jsonSerializer, mediaEncoder) { _httpClient = httpClient; } @@ -232,7 +234,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun int? width = null; int? height = null; bool isInterlaced = true; - var videoCodec = "mpeg2video"; + var videoCodec = !string.IsNullOrWhiteSpace(GetEncodingOptions().HardwareVideoDecoder) ? null : "mpeg2video"; + int? videoBitrate = null; if (string.Equals(profile, "mobile", StringComparison.OrdinalIgnoreCase)) @@ -326,8 +329,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun BitRate = 128000 } }, - RequiresOpening = true, - RequiresClosing = true, + RequiresOpening = false, + RequiresClosing = false, BufferMs = 1000, Container = "ts", Id = profile, @@ -339,6 +342,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun return mediaSource; } + protected EncodingOptions GetEncodingOptions() + { + return Config.GetConfiguration("encoding"); + } + protected override async Task> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken) { var list = new List(); @@ -404,5 +412,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun await GetChannels(info, false, CancellationToken.None).ConfigureAwait(false); } } + + protected override async Task IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) + { + var info = await GetTunerInfos(tuner, cancellationToken).ConfigureAwait(false); + + return info.Any(i => i.Status == LiveTvTunerStatus.Available); + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index afd41e3d8..ddbbb030d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -25,7 +25,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts private readonly IFileSystem _fileSystem; private readonly IHttpClient _httpClient; - public M3UTunerHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) : base(config, logger, jsonSerializer, mediaEncoder) + public M3UTunerHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) + : base(config, logger, jsonSerializer, mediaEncoder) { _fileSystem = fileSystem; _httpClient = httpClient; @@ -209,5 +210,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts } return new List { }; } + + protected override Task IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) + { + return Task.FromResult(true); + } } }