aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
diff options
context:
space:
mode:
authorstefan <stefan@hegedues.at>2018-09-12 19:26:21 +0200
committerstefan <stefan@hegedues.at>2018-09-12 19:26:21 +0200
commit48facb797ed912e4ea6b04b17d1ff190ac2daac4 (patch)
tree8dae77a31670a888d733484cb17dd4077d5444e8 /Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
parentc32d8656382a0eacb301692e0084377fc433ae9b (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.cs179
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);