mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-07-09 07:10:34 +02:00
update tuner pooling
This commit is contained in:
parent
2c050d05f8
commit
8c32aefb53
|
@ -33,20 +33,18 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the channel stream.
|
/// Gets the channel stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="info">The information.</param>
|
|
||||||
/// <param name="channelId">The channel identifier.</param>
|
/// <param name="channelId">The channel identifier.</param>
|
||||||
/// <param name="streamId">The stream identifier.</param>
|
/// <param name="streamId">The stream identifier.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task<MediaSourceInfo>.</returns>
|
/// <returns>Task<MediaSourceInfo>.</returns>
|
||||||
Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken);
|
Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the channel stream media sources.
|
/// Gets the channel stream media sources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="info">The information.</param>
|
|
||||||
/// <param name="channelId">The channel identifier.</param>
|
/// <param name="channelId">The channel identifier.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task<List<MediaSourceInfo>>.</returns>
|
/// <returns>Task<List<MediaSourceInfo>>.</returns>
|
||||||
Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken);
|
Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates the specified information.
|
/// Validates the specified information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using MediaBrowser.Common;
|
using MediaBrowser.Common;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Extensions;
|
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
|
@ -13,7 +12,6 @@ using MediaBrowser.Model.Serialization;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -161,20 +159,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
return GetChannelsAsync(false, cancellationToken);
|
return GetChannelsAsync(false, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Tuple<ITunerHost, TunerHostInfo>> GetTunerHosts()
|
|
||||||
{
|
|
||||||
return GetConfiguration().TunerHosts
|
|
||||||
.Where(i => i.IsEnabled)
|
|
||||||
.Select(i =>
|
|
||||||
{
|
|
||||||
var provider = _liveTvManager.TunerHosts.FirstOrDefault(l => string.Equals(l.Type, i.Type, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
return provider == null ? null : new Tuple<ITunerHost, TunerHostInfo>(provider, i);
|
|
||||||
})
|
|
||||||
.Where(i => i != null)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken)
|
public Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var timers = _timerProvider.GetAll().Where(i => string.Equals(i.SeriesTimerId, timerId, StringComparison.OrdinalIgnoreCase));
|
var timers = _timerProvider.GetAll().Where(i => string.Equals(i.SeriesTimerId, timerId, StringComparison.OrdinalIgnoreCase));
|
||||||
|
@ -409,22 +393,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
{
|
{
|
||||||
_logger.Info("Streaming Channel " + channelId);
|
_logger.Info("Streaming Channel " + channelId);
|
||||||
|
|
||||||
foreach (var hostInstance in GetTunerHosts())
|
foreach (var hostInstance in _liveTvManager.TunerHosts)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(streamId))
|
|
||||||
{
|
|
||||||
var originalStreamId = string.Join("-", streamId.Split('-').Skip(1).ToArray());
|
|
||||||
|
|
||||||
if (!string.Equals(hostInstance.Item2.Id, originalStreamId, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaSourceInfo mediaSourceInfo = null;
|
MediaSourceInfo mediaSourceInfo = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mediaSourceInfo = await hostInstance.Item1.GetChannelStream(hostInstance.Item2, channelId, streamId, cancellationToken).ConfigureAwait(false);
|
mediaSourceInfo = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -443,16 +417,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
|
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
foreach (var hostInstance in GetTunerHosts())
|
foreach (var hostInstance in _liveTvManager.TunerHosts)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sources = await hostInstance.Item1.GetChannelStreamMediaSources(hostInstance.Item2, channelId, cancellationToken).ConfigureAwait(false);
|
var sources = await hostInstance.GetChannelStreamMediaSources(channelId, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
foreach (var source in sources)
|
|
||||||
{
|
|
||||||
source.Id = hostInstance.Item2.Id + "-" + source.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sources.Count > 0)
|
if (sources.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
@ -83,11 +84,115 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (IsValidChannelId(channelId))
|
||||||
|
{
|
||||||
|
var hosts = GetTunerHosts();
|
||||||
|
|
||||||
|
var hostsWithChannel = new List<TunerHostInfo>();
|
||||||
|
|
||||||
|
foreach (var host in hosts)
|
||||||
|
{
|
||||||
|
var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
hostsWithChannel.Add(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var host in hostsWithChannel)
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
foreach (var mediaSource in mediaSources)
|
||||||
|
{
|
||||||
|
mediaSource.Id = host.Id + mediaSource.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mediaSources;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<MediaSourceInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (IsValidChannelId(channelId))
|
||||||
|
{
|
||||||
|
var hosts = GetTunerHosts();
|
||||||
|
|
||||||
|
var hostsWithChannel = new List<TunerHostInfo>();
|
||||||
|
|
||||||
|
foreach (var host in hosts)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(streamId))
|
||||||
|
{
|
||||||
|
var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
hostsWithChannel.Add(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (streamId.StartsWith(host.Id, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
hostsWithChannel = new List<TunerHostInfo> { host };
|
||||||
|
streamId = streamId.Substring(host.Id.Length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var host in hostsWithChannel)
|
||||||
|
{
|
||||||
|
// 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");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (stream != null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new LiveTvConflictException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Task<bool> IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
protected abstract bool IsValidChannelId(string channelId);
|
||||||
|
|
||||||
protected LiveTvOptions GetConfiguration()
|
protected LiveTvOptions GetConfiguration()
|
||||||
{
|
{
|
||||||
return Config.GetConfiguration<LiveTvOptions>("livetv");
|
return Config.GetConfiguration<LiveTvOptions>("livetv");
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChannelCache
|
private class ChannelCache
|
||||||
{
|
{
|
||||||
public DateTime Date;
|
public DateTime Date;
|
||||||
|
|
|
@ -329,7 +329,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
return mediaSource;
|
return mediaSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
|
protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var list = new List<MediaSourceInfo>();
|
var list = new List<MediaSourceInfo>();
|
||||||
|
|
||||||
|
@ -364,7 +364,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
|
protected override bool IsValidChannelId(string channelId)
|
||||||
|
{
|
||||||
|
return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase))
|
if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
@ -379,5 +384,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
{
|
{
|
||||||
await GetChannels(info, false, CancellationToken.None).ConfigureAwait(false);
|
await GetChannels(info, false, CancellationToken.None).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override async Task<bool> IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
{
|
{
|
||||||
public class M3UTunerHost : BaseTunerHost, ITunerHost
|
public class M3UTunerHost : BaseTunerHost, ITunerHost
|
||||||
{
|
{
|
||||||
public M3UTunerHost(IConfigurationManager config, ILogger logger) : base(config, logger)
|
public M3UTunerHost(IConfigurationManager config, ILogger logger)
|
||||||
|
: base(config, logger)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +32,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
get { return "M3U Tuner"; }
|
get { return "M3U Tuner"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string ChannelIdPrefix = "m3u_";
|
||||||
|
|
||||||
protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
|
protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var url = info.Url;
|
var url = info.Url;
|
||||||
|
@ -88,7 +91,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
switch (list[0])
|
switch (list[0])
|
||||||
{
|
{
|
||||||
case "tvg-id":
|
case "tvg-id":
|
||||||
channels.Last().Id = urlHash + list[1];
|
channels.Last().Id = ChannelIdPrefix + urlHash + list[1];
|
||||||
channels.Last().Number = list[1];
|
channels.Last().Number = list[1];
|
||||||
break;
|
break;
|
||||||
case "tvg-name":
|
case "tvg-name":
|
||||||
|
@ -117,19 +120,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
Url = i.Url
|
Url = i.Url
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
return Task.FromResult(list);
|
return Task.FromResult(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
|
protected override async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var urlHash = info.Url.GetMD5().ToString("N");
|
var urlHash = info.Url.GetMD5().ToString("N");
|
||||||
if (!channelId.StartsWith(urlHash, StringComparison.OrdinalIgnoreCase))
|
var prefix = ChannelIdPrefix + urlHash;
|
||||||
|
if (!channelId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
channelId = channelId.Substring(urlHash.Length);
|
channelId = channelId.Substring(prefix.Length);
|
||||||
|
|
||||||
var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false);
|
var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false);
|
||||||
var m3uchannels = channels.Cast<M3UChannel>();
|
var m3uchannels = channels.Cast<M3UChannel>();
|
||||||
|
@ -196,9 +200,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
|
protected override bool IsValidChannelId(string channelId)
|
||||||
|
{
|
||||||
|
return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Task<bool> IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue