aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2015-03-29 00:56:39 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2015-03-29 00:56:39 -0400
commit578dec0c71361be17eed68f82f20840807a9c9f4 (patch)
tree3053858f175c2bfca6eb44df95723cf7c2e4876d /MediaBrowser.Server.Implementations
parentbd2ea703e31522d505407a33089b95f997f6b062 (diff)
update stream generation
Diffstat (limited to 'MediaBrowser.Server.Implementations')
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs4
-rw-r--r--MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs155
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs27
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs51
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs8
5 files changed, 208 insertions, 37 deletions
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
index 6a7163bb3b..dac3a80f2c 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
+++ b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
@@ -30,12 +30,12 @@ namespace MediaBrowser.Server.Implementations.Channels
return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
}
- public Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
+ public Task<MediaSourceInfo> OpenMediaSource(string openToken, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
- public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
+ public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
index 40cf240d7e..3dbcf4aad4 100644
--- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
@@ -207,14 +207,14 @@ namespace MediaBrowser.Server.Implementations.Library
{
var prefix = provider.GetType().FullName.GetMD5().ToString("N") + "|";
- if (!string.IsNullOrWhiteSpace(mediaSource.OpenKey) && !mediaSource.OpenKey.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrWhiteSpace(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
- mediaSource.OpenKey = prefix + mediaSource.OpenKey;
+ mediaSource.OpenToken = prefix + mediaSource.OpenToken;
}
- if (!string.IsNullOrWhiteSpace(mediaSource.CloseKey) && !mediaSource.CloseKey.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrWhiteSpace(mediaSource.LiveStreamId) && !mediaSource.LiveStreamId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
- mediaSource.CloseKey = prefix + mediaSource.CloseKey;
+ mediaSource.LiveStreamId = prefix + mediaSource.LiveStreamId;
}
}
@@ -314,24 +314,41 @@ namespace MediaBrowser.Server.Implementations.Library
return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
}
- private readonly ConcurrentDictionary<string, string> _openStreams =
- new ConcurrentDictionary<string, string>();
+ private readonly ConcurrentDictionary<string, LiveStreamInfo> _openStreams = new ConcurrentDictionary<string, LiveStreamInfo>();
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
- public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
+
+ public async Task<MediaSourceInfo> OpenLiveStream(string openToken, bool enableAutoClose, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
- var tuple = GetProvider(openKey);
+ var tuple = GetProvider(openToken);
var provider = tuple.Item1;
var mediaSource = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
SetKeyProperties(provider, mediaSource);
- _openStreams.AddOrUpdate(mediaSource.CloseKey, mediaSource.CloseKey, (key, i) => mediaSource.CloseKey);
-
+ var info = new LiveStreamInfo
+ {
+ Date = DateTime.UtcNow,
+ EnableCloseTimer = enableAutoClose,
+ Id = mediaSource.LiveStreamId,
+ MediaSource = mediaSource
+ };
+ _openStreams.AddOrUpdate(mediaSource.LiveStreamId, info, (key, i) => info);
+
+ if (enableAutoClose)
+ {
+ StartCloseTimer();
+ }
+
+ if (!string.IsNullOrWhiteSpace(mediaSource.TranscodingUrl))
+ {
+ mediaSource.TranscodingUrl += "&LiveStreamId=" + mediaSource.LiveStreamId;
+ }
+
return mediaSource;
}
finally
@@ -340,18 +357,70 @@ namespace MediaBrowser.Server.Implementations.Library
}
}
- public async Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
+ public async Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken)
+ {
+ await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ try
+ {
+ LiveStreamInfo info;
+ if (_openStreams.TryGetValue(id, out info))
+ {
+ return info.MediaSource;
+ }
+ else
+ {
+ throw new ResourceNotFoundException();
+ }
+ }
+ finally
+ {
+ _liveStreamSemaphore.Release();
+ }
+ }
+
+ public async Task PingLiveStream(string id, CancellationToken cancellationToken)
+ {
+ await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ try
+ {
+ LiveStreamInfo info;
+ if (_openStreams.TryGetValue(id, out info))
+ {
+ info.Date = DateTime.UtcNow;
+ }
+ else
+ {
+ _logger.Error("Failed to update MediaSource timestamp for {0}", id);
+ }
+ }
+ finally
+ {
+ _liveStreamSemaphore.Release();
+ }
+ }
+
+ public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
- var tuple = GetProvider(closeKey);
+ var tuple = GetProvider(id);
- await tuple.Item1.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
+ await tuple.Item1.CloseMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
- string removedKey;
- _openStreams.TryRemove(closeKey, out removedKey);
+ LiveStreamInfo removed;
+ if (_openStreams.TryRemove(id, out removed))
+ {
+ removed.Closed = true;
+ }
+
+ if (_openStreams.Count == 0)
+ {
+ StopCloseTimer();
+ }
}
finally
{
@@ -368,11 +437,56 @@ namespace MediaBrowser.Server.Implementations.Library
return new Tuple<IMediaSourceProvider, string>(provider, keys[1]);
}
+ private Timer _closeTimer;
+ private readonly TimeSpan _openStreamMaxAge = TimeSpan.FromSeconds(40);
+
+ private void StartCloseTimer()
+ {
+ StopCloseTimer();
+
+ _closeTimer = new Timer(CloseTimerCallback, null, _openStreamMaxAge, _openStreamMaxAge);
+ }
+
+ private void StopCloseTimer()
+ {
+ var timer = _closeTimer;
+
+ if (timer != null)
+ {
+ _closeTimer = null;
+ timer.Dispose();
+ }
+ }
+
+ private async void CloseTimerCallback(object state)
+ {
+ var infos = _openStreams
+ .Values
+ .Where(i => i.EnableCloseTimer && (DateTime.UtcNow - i.Date) > _openStreamMaxAge)
+ .ToList();
+
+ foreach (var info in infos)
+ {
+ if (!info.Closed)
+ {
+ try
+ {
+ await CloseLiveStream(info.Id, CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error closing media source", ex);
+ }
+ }
+ }
+ }
+
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
+ StopCloseTimer();
Dispose(true);
}
@@ -389,7 +503,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
foreach (var key in _openStreams.Keys.ToList())
{
- var task = CloseMediaSource(key, CancellationToken.None);
+ var task = CloseLiveStream(key, CancellationToken.None);
Task.WaitAll(task);
}
@@ -398,5 +512,14 @@ namespace MediaBrowser.Server.Implementations.Library
}
}
}
+
+ private class LiveStreamInfo
+ {
+ public DateTime Date;
+ public bool EnableCloseTimer;
+ public string Id;
+ public bool Closed;
+ public MediaSourceInfo MediaSource;
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 202a051e3b..86b31e0e5d 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -313,6 +313,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return await GetLiveStream(id, true, cancellationToken).ConfigureAwait(false);
}
+ public async Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(string id, CancellationToken cancellationToken)
+ {
+ var item = await GetInternalRecording(id, cancellationToken).ConfigureAwait(false);
+ var service = GetService(item);
+
+ return await service.GetRecordingStreamMediaSources(id, cancellationToken).ConfigureAwait(false);
+ }
+
+ public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(string id, CancellationToken cancellationToken)
+ {
+ var item = GetInternalChannel(id);
+ var service = GetService(item);
+
+ return await service.GetChannelStreamMediaSources(id, cancellationToken).ConfigureAwait(false);
+ }
+
private ILiveTvService GetService(ILiveTvItem item)
{
return GetService(item.ServiceName);
@@ -330,7 +346,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
try
{
MediaSourceInfo info;
- var isVideo = true;
+ bool isVideo;
if (isChannel)
{
@@ -340,7 +356,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
info = await service.GetChannelStream(channel.ExternalId, null, cancellationToken).ConfigureAwait(false);
info.RequiresClosing = true;
- info.CloseKey = info.Id;
+ info.LiveStreamId = info.Id;
}
else
{
@@ -351,7 +367,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_logger.Info("Opening recording stream from {0}, external recording Id: {1}", service.Name, recording.RecordingInfo.Id);
info = await service.GetRecordingStream(recording.RecordingInfo.Id, null, cancellationToken).ConfigureAwait(false);
info.RequiresClosing = true;
- info.CloseKey = info.Id;
+ info.LiveStreamId = info.Id;
}
_logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info));
@@ -393,7 +409,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
Type = MediaStreamType.Video,
// Set the index to -1 because we don't know the exact index of the video stream within the container
- Index = -1
+ Index = -1,
+
+ // Set to true if unknown to enable deinterlacing
+ IsInterlaced = true
},
new MediaStream
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 186bc499db..5de4cf4998 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -2,6 +2,8 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,10 +15,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public class LiveTvMediaSourceProvider : IMediaSourceProvider
{
private readonly ILiveTvManager _liveTvManager;
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly ILogger _logger;
- public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager)
+ public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IJsonSerializer jsonSerializer, ILogManager logManager)
{
_liveTvManager = liveTvManager;
+ _jsonSerializer = jsonSerializer;
+ _logger = logManager.GetLogger(GetType().Name);
}
public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
@@ -38,28 +44,51 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(ILiveTvItem item, CancellationToken cancellationToken)
{
- var hasMediaSources = (IHasMediaSources)item;
+ IEnumerable<MediaSourceInfo> sources;
- var sources = hasMediaSources.GetMediaSources(false)
- .ToList();
+ try
+ {
+ if (item is ILiveTvRecording)
+ {
+ sources = await _liveTvManager.GetRecordingMediaSources(item.Id.ToString("N"), cancellationToken)
+ .ConfigureAwait(false);
+ }
+ else
+ {
+ sources = await _liveTvManager.GetChannelMediaSources(item.Id.ToString("N"), cancellationToken)
+ .ConfigureAwait(false);
+ }
+ }
+ catch (NotImplementedException)
+ {
+ var hasMediaSources = (IHasMediaSources)item;
+
+ sources = hasMediaSources.GetMediaSources(false)
+ .ToList();
+ }
- foreach (var source in sources)
+ var list = sources.ToList();
+
+ foreach (var source in list)
{
source.Type = MediaSourceType.Default;
source.RequiresOpening = true;
+ source.BufferMs = source.BufferMs ?? 1500;
var openKeys = new List<string>();
openKeys.Add(item.GetType().Name);
openKeys.Add(item.Id.ToString("N"));
- source.OpenKey = string.Join("|", openKeys.ToArray());
+ source.OpenToken = string.Join("|", openKeys.ToArray());
}
- return sources;
+ _logger.Debug("MediaSources: {0}", _jsonSerializer.SerializeToString(list));
+
+ return list;
}
- public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
+ public async Task<MediaSourceInfo> OpenMediaSource(string openToken, CancellationToken cancellationToken)
{
- var keys = openKey.Split(new[] { '|' }, 2);
+ var keys = openToken.Split(new[] { '|' }, 2);
if (string.Equals(keys[0], typeof(LiveTvChannel).Name, StringComparison.OrdinalIgnoreCase))
{
@@ -69,9 +98,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return await _liveTvManager.GetRecordingStream(keys[1], cancellationToken).ConfigureAwait(false);
}
- public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
+ public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken)
{
- return _liveTvManager.CloseLiveStream(closeKey, cancellationToken);
+ return _liveTvManager.CloseLiveStream(liveStreamId, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
index 25a52fb950..1c17b99936 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
@@ -90,13 +90,13 @@ namespace MediaBrowser.Server.Implementations.Sync
keyList.Add(provider.GetType().FullName.GetMD5().ToString("N"));
keyList.Add(target.Id.GetMD5().ToString("N"));
keyList.Add(item.Id);
- mediaSource.OpenKey = string.Join("|", keyList.ToArray());
+ mediaSource.OpenToken = string.Join("|", keyList.ToArray());
}
}
- public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
+ public async Task<MediaSourceInfo> OpenMediaSource(string openToken, CancellationToken cancellationToken)
{
- var openKeys = openKey.Split(new[] { '|' }, 3);
+ var openKeys = openToken.Split(new[] { '|' }, 3);
var provider = _syncManager.ServerSyncProviders
.FirstOrDefault(i => string.Equals(openKeys[0], i.GetType().FullName.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase));
@@ -133,7 +133,7 @@ namespace MediaBrowser.Server.Implementations.Sync
mediaSource.SupportsTranscoding = false;
}
- public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
+ public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}