diff options
| author | stefan <stefan@hegedues.at> | 2018-09-12 19:26:21 +0200 |
|---|---|---|
| committer | stefan <stefan@hegedues.at> | 2018-09-12 19:26:21 +0200 |
| commit | 48facb797ed912e4ea6b04b17d1ff190ac2daac4 (patch) | |
| tree | 8dae77a31670a888d733484cb17dd4077d5444e8 /Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs | |
| parent | c32d8656382a0eacb301692e0084377fc433ae9b (diff) | |
Update to 3.5.2 and .net core 2.1
Diffstat (limited to 'Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs')
| -rw-r--r-- | Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs | 179 |
1 files changed, 116 insertions, 63 deletions
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index f6758e94e2..7e0ac4131d 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -11,47 +11,52 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.System; using MediaBrowser.Model.LiveTv; +using System.Linq; +using MediaBrowser.Controller.Library; 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 MediaSourceInfo MediaSource { get; set; } + + public int ConsumerCount { 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 IServerApplicationPaths AppPaths; - protected string TempFilePath; + protected string TempFilePath; protected readonly ILogger Logger; protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource(); public string TunerHostId { get; private set; } - public LiveStream(MediaSourceInfo mediaSource, TunerHostInfo tuner, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths) + public DateTime DateOpened { get; protected set; } + + public Func<Task> OnClose { get; set; } + + public LiveStream(MediaSourceInfo mediaSource, TunerHostInfo tuner, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths) { OriginalMediaSource = mediaSource; - Environment = environment; FileSystem = fileSystem; - OpenedMediaSource = mediaSource; + MediaSource = mediaSource; Logger = logger; EnableStreamSharing = true; - SharedStreamIds = new List<string>(); UniqueId = Guid.NewGuid().ToString("N"); - TunerHostId = tuner.Id; + + if (tuner != null) + { + TunerHostId = tuner.Id; + } AppPaths = appPaths; + ConsumerCount = 1; SetTempFilePath("ts"); } @@ -62,20 +67,36 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public virtual Task Open(CancellationToken openCancellationToken) { - return Task.FromResult(true); + DateOpened = DateTime.UtcNow; + return Task.CompletedTask; } - public void Close() + public Task Close() { EnableStreamSharing = false; Logger.Info("Closing " + GetType().Name); - CloseInternal(); + LiveStreamCancellationTokenSource.Cancel(); + + if (OnClose != null) + { + return CloseWithExternalFn(); + } + + return Task.CompletedTask; } - protected virtual void CloseInternal() + private async Task CloseWithExternalFn() { + try + { + await OnClose().ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.ErrorException("Error closing live stream", ex); + } } protected Stream GetInputStream(string path, bool allowAsyncFileRead) @@ -90,91 +111,123 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions); } - protected async Task DeleteTempFile(string path, int retryCount = 0) + public Task DeleteTempFiles() + { + return DeleteTempFiles(GetStreamFilePaths()); + } + + protected async Task DeleteTempFiles(List<string> paths, int retryCount = 0) { if (retryCount == 0) { - Logger.Info("Deleting temp file {0}", path); + Logger.Info("Deleting temp files {0}", string.Join(", ", paths.ToArray())); } - try - { - FileSystem.DeleteFile(path); - return; - } - catch (DirectoryNotFoundException) - { - return; - } - catch (FileNotFoundException) - { - return; - } - catch - { + var failedFiles = new List<string>(); + foreach (var path in paths) + { + try + { + FileSystem.DeleteFile(path); + } + catch (DirectoryNotFoundException) + { + } + catch (FileNotFoundException) + { + } + catch (Exception ex) + { + //Logger.ErrorException("Error deleting file {0}", ex, path); + failedFiles.Add(path); + } } - if (retryCount > 20) + if (failedFiles.Count > 0 && retryCount <= 40) { - return; + await Task.Delay(500).ConfigureAwait(false); + await DeleteTempFiles(failedFiles, retryCount + 1).ConfigureAwait(false); } + } - await Task.Delay(500).ConfigureAwait(false); - await DeleteTempFile(path, retryCount + 1).ConfigureAwait(false); + protected virtual List<string> GetStreamFilePaths() + { + return new List<string> { TempFilePath }; } public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken) { cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token).Token; - var allowAsync = false;//Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows; + var allowAsync = false; // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 - using (var inputStream = (FileStream)GetInputStream(TempFilePath, allowAsync)) + bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10; + + var nextFileInfo = GetNextFile(null); + var nextFile = nextFileInfo.Item1; + var isLastFile = nextFileInfo.Item2; + + while (!string.IsNullOrEmpty(nextFile)) { - TrySeek(inputStream, -20000); + var emptyReadLimit = isLastFile ? EmptyReadLimit : 1; + + await CopyFile(nextFile, seekFile, emptyReadLimit, allowAsync, stream, cancellationToken).ConfigureAwait(false); - await CopyTo(inputStream, stream, 81920, null, cancellationToken).ConfigureAwait(false); + seekFile = false; + nextFileInfo = GetNextFile(nextFile); + nextFile = nextFileInfo.Item1; + isLastFile = nextFileInfo.Item2; } + + Logger.Info("Live Stream ended."); } - private static async Task CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken) + private Tuple<string, bool> GetNextFile(string currentFile) { - byte[] buffer = new byte[bufferSize]; + var files = GetStreamFilePaths(); - var eofCount = 0; - var emptyReadLimit = 1000; + //Logger.Info("Live stream files: {0}", string.Join(", ", files.ToArray())); - while (eofCount < emptyReadLimit) + if (string.IsNullOrEmpty(currentFile)) { - cancellationToken.ThrowIfCancellationRequested(); + return new Tuple<string, bool>(files.Last(), true); + } + + var nextIndex = files.FindIndex(i => string.Equals(i, currentFile, StringComparison.OrdinalIgnoreCase)) + 1; + + var isLastFile = nextIndex == files.Count - 1; - var bytesRead = source.Read(buffer, 0, buffer.Length); + return new Tuple<string, bool>(files.ElementAtOrDefault(nextIndex), isLastFile); + } + + private async Task CopyFile(string path, bool seekFile, int emptyReadLimit, bool allowAsync, Stream stream, CancellationToken cancellationToken) + { + //Logger.Info("Opening live stream file {0}. Empty read limit: {1}", path, emptyReadLimit); - if (bytesRead == 0) + using (var inputStream = (FileStream)GetInputStream(path, allowAsync)) + { + if (seekFile) { - eofCount++; - await Task.Delay(10, cancellationToken).ConfigureAwait(false); + TrySeek(inputStream, -20000); } - else - { - eofCount = 0; - //await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false); - destination.Write(buffer, 0, bytesRead); + await ApplicationHost.StreamHelper.CopyToAsync(inputStream, stream, 81920, emptyReadLimit, cancellationToken).ConfigureAwait(false); + } + } - if (onStarted != null) - { - onStarted(); - onStarted = null; - } - } + protected virtual int EmptyReadLimit + { + get + { + return 1000; } } private void TrySeek(FileStream stream, long offset) { + //Logger.Info("TrySeek live stream"); try { stream.Seek(offset, SeekOrigin.End); |
