jellyfin/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs

197 lines
6.0 KiB
C#
Raw Normal View History

2017-09-28 17:02:49 +00:00
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;
using MediaBrowser.Model.LiveTv;
2017-09-28 17:02:49 +00:00
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; }
}
2017-09-28 17:02:49 +00:00
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;
2017-11-14 07:41:21 +00:00
protected readonly IServerApplicationPaths AppPaths;
2017-09-28 17:02:49 +00:00
2017-11-14 07:41:21 +00:00
protected string TempFilePath;
2017-09-28 17:02:49 +00:00
protected readonly ILogger Logger;
2017-10-14 06:52:56 +00:00
protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource();
2017-09-28 17:02:49 +00:00
public string TunerHostId { get; private set; }
public LiveStream(MediaSourceInfo mediaSource, TunerHostInfo tuner, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths)
2017-09-28 17:02:49 +00:00
{
OriginalMediaSource = mediaSource;
Environment = environment;
FileSystem = fileSystem;
OpenedMediaSource = mediaSource;
Logger = logger;
EnableStreamSharing = true;
SharedStreamIds = new List<string>();
UniqueId = Guid.NewGuid().ToString("N");
TunerHostId = tuner.Id;
2017-11-14 07:41:21 +00:00
AppPaths = appPaths;
SetTempFilePath("ts");
}
protected void SetTempFilePath(string extension)
{
TempFilePath = Path.Combine(AppPaths.GetTranscodingTempPath(), UniqueId + "." + extension);
2017-09-28 17:02:49 +00:00
}
2017-10-23 19:14:11 +00:00
public virtual Task Open(CancellationToken openCancellationToken)
2017-09-28 17:02:49 +00:00
{
return Task.FromResult(true);
}
public void Close()
{
EnableStreamSharing = false;
Logger.Info("Closing " + GetType().Name);
CloseInternal();
}
protected virtual void CloseInternal()
2017-09-28 17:02:49 +00:00
{
}
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)
{
2017-10-23 19:14:11 +00:00
if (retryCount == 0)
{
Logger.Info("Deleting temp file {0}", path);
}
2017-09-28 17:02:49 +00:00
try
{
FileSystem.DeleteFile(path);
return;
}
2017-10-14 06:52:56 +00:00
catch (DirectoryNotFoundException)
{
return;
}
catch (FileNotFoundException)
{
return;
}
2017-09-28 17:02:49 +00:00
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)
{
2017-10-14 06:52:56 +00:00
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token).Token;
2017-09-28 17:02:49 +00:00
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];
2017-10-14 06:52:56 +00:00
var eofCount = 0;
var emptyReadLimit = 1000;
while (eofCount < emptyReadLimit)
2017-09-28 17:02:49 +00:00
{
cancellationToken.ThrowIfCancellationRequested();
2017-10-14 06:52:56 +00:00
var bytesRead = source.Read(buffer, 0, buffer.Length);
2017-09-28 17:02:49 +00:00
2017-10-14 06:52:56 +00:00
if (bytesRead == 0)
{
eofCount++;
await Task.Delay(10, cancellationToken).ConfigureAwait(false);
}
else
2017-09-28 17:02:49 +00:00
{
2017-10-14 06:52:56 +00:00
eofCount = 0;
2017-09-28 17:02:49 +00:00
//await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false);
2017-10-14 06:52:56 +00:00
destination.Write(buffer, 0, bytesRead);
2017-09-28 17:02:49 +00:00
if (onStarted != null)
{
onStarted();
onStarted = null;
}
}
}
}
private void TrySeek(FileStream stream, long offset)
{
try
{
stream.Seek(offset, SeekOrigin.End);
2017-10-05 18:10:46 +00:00
}
catch (IOException)
{
2017-09-28 17:02:49 +00:00
}
catch (ArgumentException)
{
}
catch (Exception ex)
{
Logger.ErrorException("Error seeking stream", ex);
}
}
}
}