diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2015-03-29 00:56:39 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2015-03-29 00:56:39 -0400 |
| commit | 578dec0c71361be17eed68f82f20840807a9c9f4 (patch) | |
| tree | 3053858f175c2bfca6eb44df95723cf7c2e4876d /MediaBrowser.Server.Implementations | |
| parent | bd2ea703e31522d505407a33089b95f997f6b062 (diff) | |
update stream generation
Diffstat (limited to 'MediaBrowser.Server.Implementations')
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(); } |
