diff options
| author | Luke <luke.pulverenti@gmail.com> | 2017-10-01 23:35:45 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-10-01 23:35:45 -0400 |
| commit | a73da532d52ae68ad01965dbf368bacc7d56a218 (patch) | |
| tree | a6f05e95515f822dfc129c36554f74f1da4d4c23 /Emby.Server.Implementations/LiveTv | |
| parent | f651fd6ffba80a6f8e54fca7d014622692cc08f7 (diff) | |
| parent | 796f374359d75e22d2095b3f3a8f9b220cacde27 (diff) | |
Merge pull request #2931 from MediaBrowser/beta
Beta
Diffstat (limited to 'Emby.Server.Implementations/LiveTv')
9 files changed, 196 insertions, 173 deletions
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 1975a6b019..4a2836d597 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1006,7 +1006,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } private readonly SemaphoreSlim _liveStreamsSemaphore = new SemaphoreSlim(1, 1); - private readonly List<LiveStream> _liveStreams = new List<LiveStream>(); + private readonly List<ILiveStream> _liveStreams = new List<ILiveStream>(); public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken) { @@ -1039,7 +1039,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return mediaSource; } - public async Task<LiveStream> GetLiveStream(string uniqueId) + public async Task<ILiveStream> GetLiveStream(string uniqueId) { await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false); @@ -1055,7 +1055,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } - private async Task<Tuple<LiveStream, MediaSourceInfo, ITunerHost>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) + private async Task<Tuple<ILiveStream, MediaSourceInfo, ITunerHost>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) { _logger.Info("Streaming Channel " + channelId); @@ -1072,7 +1072,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount); - return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost); + return new Tuple<ILiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost); } foreach (var hostInstance in _liveTvManager.TunerHosts) @@ -1092,7 +1092,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}", streamId, openedMediaSource.Id, openedMediaSource.LiveStreamId); - return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, hostInstance); + return new Tuple<ILiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, hostInstance); } catch (FileNotFoundException) { @@ -1718,7 +1718,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { var parent = _fileSystem.GetDirectoryName(originalPath); var name = Path.GetFileNameWithoutExtension(originalPath); - name += "-" + index.ToString(CultureInfo.InvariantCulture); + name += " - " + index.ToString(CultureInfo.InvariantCulture); path = Path.ChangeExtension(Path.Combine(parent, name), Path.GetExtension(originalPath)); index++; diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index fb8308cda5..8ea98879a6 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings if (!path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { - return path; + return UnzipIfNeeded(path, path); } var cacheFilename = DateTime.UtcNow.DayOfYear.ToString(CultureInfo.InvariantCulture) + "-" + DateTime.UtcNow.Hour.ToString(CultureInfo.InvariantCulture) + ".xml"; @@ -94,46 +94,30 @@ namespace Emby.Server.Implementations.LiveTv.Listings _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFile)); - using (var stream = _fileSystem.OpenRead(tempFile)) - { - using (var reader = new StreamReader(stream, Encoding.UTF8)) - { - using (var fileStream = _fileSystem.GetFileStream(cacheFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) - { - using (var writer = new StreamWriter(fileStream)) - { - while (!reader.EndOfStream) - { - writer.WriteLine(reader.ReadLine()); - } - } - } - } - } + _fileSystem.CopyFile(tempFile, cacheFile, true); - _logger.Debug("Returning xmltv path {0}", cacheFile); return UnzipIfNeeded(path, cacheFile); } private string UnzipIfNeeded(string originalUrl, string file) { - //var ext = Path.GetExtension(originalUrl); - - //if (string.Equals(ext, ".gz", StringComparison.OrdinalIgnoreCase)) - //{ - // using (var stream = _fileSystem.OpenRead(file)) - // { - // var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString()); - // _fileSystem.CreateDirectory(tempFolder); - - // _zipClient.ExtractAllFromZip(stream, tempFolder, true); - - // return _fileSystem.GetFiles(tempFolder, true) - // .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase)) - // .Select(i => i.FullName) - // .FirstOrDefault(); - // } - //} + var ext = Path.GetExtension(originalUrl.Split('?')[0]); + + if (string.Equals(ext, ".gz", StringComparison.OrdinalIgnoreCase)) + { + using (var stream = _fileSystem.OpenRead(file)) + { + var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString()); + _fileSystem.CreateDirectory(tempFolder); + + _zipClient.ExtractAllFromGz(stream, tempFolder, true); + + return _fileSystem.GetFiles(tempFolder, true) + .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase)) + .Select(i => i.FullName) + .FirstOrDefault(); + } + } return file; } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 38d2fd3c64..857afa3781 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.LiveTv return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id); } - public Task<LiveStream> GetEmbyTvLiveStream(string id) + public Task<ILiveStream> GetEmbyTvLiveStream(string id) { return EmbyTV.EmbyTV.Current.GetLiveStream(id); } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 4b4f61d535..e0fd32aeef 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -193,9 +193,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return new List<MediaSourceInfo>(); } - protected abstract Task<LiveStream> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken); + protected abstract Task<ILiveStream> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken); - public async Task<LiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken) + public async Task<ILiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(channelId)) { @@ -247,7 +247,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts try { var liveStream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); + var startTime = DateTime.UtcNow; await liveStream.Open(cancellationToken).ConfigureAwait(false); + var endTime = DateTime.UtcNow; + Logger.Info("Live stream opened after {0}ms", (endTime - startTime).TotalMilliseconds); return liveStream; } catch (Exception ex) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index f974b5c2c7..bb11dac5f1 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -347,6 +347,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun videoCodec = "h264"; videoBitrate = 1000000; } + else + { + // This is for android tv's 1200 condition. Remove once not needed anymore so that we can avoid possible side effects of dummying up this data + if ((channelInfo.IsHD ?? true)) + { + width = 1920; + height = 1080; + } + } if (channelInfo != null) { @@ -491,7 +500,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return list; } - protected override async Task<LiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) + protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) { var profile = streamId.Split('_')[0]; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs new file mode 100644 index 0000000000..4641a1c91f --- /dev/null +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller; +using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.System; + +namespace Emby.Server.Implementations.LiveTv.TunerHosts +{ + public class LiveStream : ILiveStream + { + public MediaSourceInfo OriginalMediaSource { get; set; } + public MediaSourceInfo OpenedMediaSource { get; set; } + public int ConsumerCount + { + get { return SharedStreamIds.Count; } + } + public ITunerHost TunerHost { get; set; } + public string OriginalStreamId { get; set; } + public bool EnableStreamSharing { get; set; } + public string UniqueId { get; private set; } + + public List<string> SharedStreamIds { get; private set; } + protected readonly IEnvironmentInfo Environment; + protected readonly IFileSystem FileSystem; + + protected readonly string TempFilePath; + protected readonly ILogger Logger; + + public LiveStream(MediaSourceInfo mediaSource, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths) + { + OriginalMediaSource = mediaSource; + Environment = environment; + FileSystem = fileSystem; + OpenedMediaSource = mediaSource; + Logger = logger; + EnableStreamSharing = true; + SharedStreamIds = new List<string>(); + UniqueId = Guid.NewGuid().ToString("N"); + TempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts"); + } + + public Task Open(CancellationToken cancellationToken) + { + return OpenInternal(cancellationToken); + } + + protected virtual Task OpenInternal(CancellationToken cancellationToken) + { + return Task.FromResult(true); + } + + public virtual Task Close() + { + return Task.FromResult(true); + } + + protected Stream GetInputStream(string path, bool allowAsyncFileRead) + { + var fileOpenOptions = FileOpenOptions.SequentialScan; + + if (allowAsyncFileRead) + { + fileOpenOptions |= FileOpenOptions.Asynchronous; + } + + return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions); + } + + protected async Task DeleteTempFile(string path, int retryCount = 0) + { + try + { + FileSystem.DeleteFile(path); + return; + } + catch + { + + } + + if (retryCount > 20) + { + return; + } + + await Task.Delay(500).ConfigureAwait(false); + await DeleteTempFile(path, retryCount + 1).ConfigureAwait(false); + } + + public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken) + { + var allowAsync = false;//Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows; + // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 + + using (var inputStream = (FileStream)GetInputStream(TempFilePath, allowAsync)) + { + TrySeek(inputStream, -20000); + + await CopyTo(inputStream, stream, 81920, null, cancellationToken).ConfigureAwait(false); + } + } + + private static async Task CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken) + { + byte[] buffer = new byte[bufferSize]; + while (true) + { + cancellationToken.ThrowIfCancellationRequested(); + + var read = source.Read(buffer, 0, buffer.Length); + + if (read > 0) + { + //await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false); + destination.Write(buffer, 0, read); + + if (onStarted != null) + { + onStarted(); + onStarted = null; + } + } + else + { + await Task.Delay(10).ConfigureAwait(false); + } + } + } + + private void TrySeek(FileStream stream, long offset) + { + try + { + stream.Seek(offset, SeekOrigin.End); + } + catch (ArgumentException) + { + + } + catch (Exception ex) + { + Logger.ErrorException("Error seeking stream", ex); + } + } + } +} diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index eac8455791..8d1854f4b2 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -75,11 +75,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return Task.FromResult(list); } - protected override async Task<LiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) + protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) { var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false); - var liveStream = new LiveStream(sources.First(), _environment, FileSystem); + var liveStream = new LiveStream(sources.First(), _environment, FileSystem, Logger, Config.ApplicationPaths); return liveStream; } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs deleted file mode 100644 index 45a0c348e1..0000000000 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; - -namespace Emby.Server.Implementations.LiveTv.TunerHosts -{ - public class MulticastStream - { - private readonly ConcurrentDictionary<Guid, QueueStream> _outputStreams = new ConcurrentDictionary<Guid, QueueStream>(); - private const int BufferSize = 81920; - private readonly ILogger _logger; - - public MulticastStream(ILogger logger) - { - _logger = logger; - } - - public async Task CopyUntilCancelled(Stream source, Action onStarted, CancellationToken cancellationToken) - { - if (source == null) - { - throw new ArgumentNullException("source"); - } - - while (true) - { - cancellationToken.ThrowIfCancellationRequested(); - - byte[] buffer = new byte[BufferSize]; - - var bytesRead = source.Read(buffer, 0, buffer.Length); - - if (bytesRead > 0) - { - foreach (var stream in _outputStreams) - { - stream.Value.Queue(buffer, 0, bytesRead); - } - - if (onStarted != null) - { - var onStartedCopy = onStarted; - onStarted = null; - Task.Run(onStartedCopy); - } - } - - else - { - await Task.Delay(100).ConfigureAwait(false); - } - } - } - - public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) - { - var queueStream = new QueueStream(stream, _logger); - - _outputStreams.TryAdd(queueStream.Id, queueStream); - - try - { - queueStream.Start(cancellationToken); - } - finally - { - _outputStreams.TryRemove(queueStream.Id, out queueStream); - GC.Collect(); - } - - return Task.FromResult(true); - } - } -} diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs deleted file mode 100644 index 07a4daa87d..0000000000 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; - -namespace Emby.Server.Implementations.LiveTv.TunerHosts -{ - public class QueueStream - { - private readonly Stream _outputStream; - private readonly BlockingCollection<Tuple<byte[], int, int>> _queue = new BlockingCollection<Tuple<byte[], int, int>>(); - - private readonly ILogger _logger; - public Guid Id = Guid.NewGuid(); - - public QueueStream(Stream outputStream, ILogger logger) - { - _outputStream = outputStream; - _logger = logger; - } - - public void Queue(byte[] bytes, int offset, int count) - { - _queue.Add(new Tuple<byte[], int, int>(bytes, offset, count)); - } - - public void Start(CancellationToken cancellationToken) - { - while (true) - { - foreach (var result in _queue.GetConsumingEnumerable()) - { - cancellationToken.ThrowIfCancellationRequested(); - - _outputStream.Write(result.Item1, result.Item2, result.Item3); - } - } - } - } -} |
