update tuner pooling

This commit is contained in:
Luke Pulverenti 2015-08-19 15:25:18 -04:00
parent 2c050d05f8
commit 8c32aefb53
5 changed files with 146 additions and 49 deletions

View file

@ -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&lt;MediaSourceInfo&gt;.</returns> /// <returns>Task&lt;MediaSourceInfo&gt;.</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&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns> /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</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>

View file

@ -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)
{ {

View file

@ -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;

View file

@ -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;
}
} }
} }

View file

@ -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);
}
} }
} }