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

165 lines
4.6 KiB
C#
Raw Normal View History

#nullable disable
#pragma warning disable CS1591
using System;
using System.Globalization;
2017-09-28 10:02:49 -07:00
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
2020-03-24 08:12:06 -07:00
using MediaBrowser.Controller.Library;
2017-09-28 10:02:49 -07:00
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using Microsoft.Extensions.Logging;
2017-09-28 10:02:49 -07:00
namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
public class LiveStream : ILiveStream
{
private readonly IConfigurationManager _configurationManager;
2017-09-28 10:02:49 -07:00
protected readonly IFileSystem FileSystem;
2019-07-07 07:39:35 -07:00
protected readonly IStreamHelper StreamHelper;
2017-09-28 10:02:49 -07:00
2018-09-12 10:26:21 -07:00
protected string TempFilePath;
2017-09-28 10:02:49 -07:00
protected readonly ILogger Logger;
2017-10-13 23:52:56 -07:00
protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource();
2017-09-28 10:02:49 -07:00
2019-07-07 07:39:35 -07:00
public LiveStream(
MediaSourceInfo mediaSource,
TunerHostInfo tuner,
IFileSystem fileSystem,
ILogger logger,
IConfigurationManager configurationManager,
2019-07-07 07:39:35 -07:00
IStreamHelper streamHelper)
2017-09-28 10:02:49 -07:00
{
OriginalMediaSource = mediaSource;
FileSystem = fileSystem;
2018-09-12 10:26:21 -07:00
MediaSource = mediaSource;
2017-09-28 10:02:49 -07:00
Logger = logger;
EnableStreamSharing = true;
UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
2018-09-12 10:26:21 -07:00
if (tuner != null)
{
TunerHostId = tuner.Id;
}
2017-11-14 00:41:21 -07:00
_configurationManager = configurationManager;
2019-07-07 07:39:35 -07:00
StreamHelper = streamHelper;
2017-11-14 00:41:21 -07:00
2018-09-12 10:26:21 -07:00
ConsumerCount = 1;
2017-11-14 00:41:21 -07:00
SetTempFilePath("ts");
}
2019-07-07 07:39:35 -07:00
protected virtual int EmptyReadLimit => 1000;
public MediaSourceInfo OriginalMediaSource { get; set; }
2020-06-15 14:43:52 -07:00
2019-07-07 07:39:35 -07:00
public MediaSourceInfo MediaSource { get; set; }
public int ConsumerCount { get; set; }
public string OriginalStreamId { get; set; }
2020-06-15 14:43:52 -07:00
2019-07-07 07:39:35 -07:00
public bool EnableStreamSharing { get; set; }
2020-06-15 14:43:52 -07:00
2019-07-07 07:39:35 -07:00
public string UniqueId { get; }
public string TunerHostId { get; }
public DateTime DateOpened { get; protected set; }
2017-11-14 00:41:21 -07:00
protected void SetTempFilePath(string extension)
{
TempFilePath = Path.Combine(_configurationManager.GetTranscodePath(), UniqueId + "." + extension);
2017-09-28 10:02:49 -07:00
}
2017-10-23 12:14:11 -07:00
public virtual Task Open(CancellationToken openCancellationToken)
2017-09-28 10:02:49 -07:00
{
2018-09-12 10:26:21 -07:00
DateOpened = DateTime.UtcNow;
return Task.CompletedTask;
2017-09-28 10:02:49 -07:00
}
2018-09-12 10:26:21 -07:00
public Task Close()
{
EnableStreamSharing = false;
2019-07-07 07:39:35 -07:00
Logger.LogInformation("Closing {Type}", GetType().Name);
2018-09-12 10:26:21 -07:00
LiveStreamCancellationTokenSource.Cancel();
return Task.CompletedTask;
}
2021-09-10 00:56:48 -07:00
public Stream GetStream()
{
var stream = GetInputStream(TempFilePath, AsyncFile.UseAsyncIO);
bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10;
if (seekFile)
{
TrySeek(stream, -20000);
}
return stream;
}
2019-07-07 07:39:35 -07:00
protected FileStream GetInputStream(string path, bool allowAsyncFileRead)
=> new FileStream(
path,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite,
2020-01-08 09:52:50 -07:00
IODefaults.FileStreamBufferSize,
2019-07-07 07:39:35 -07:00
allowAsyncFileRead ? FileOptions.SequentialScan | FileOptions.Asynchronous : FileOptions.SequentialScan);
2017-09-28 10:02:49 -07:00
protected async Task DeleteTempFiles(string path, int retryCount = 0)
2017-09-28 10:02:49 -07:00
{
2017-10-23 12:14:11 -07:00
if (retryCount == 0)
{
Logger.LogInformation("Deleting temp file {FilePath}", path);
2017-09-28 10:02:49 -07:00
}
try
2017-09-28 10:02:49 -07:00
{
FileSystem.DeleteFile(path);
2018-09-12 10:26:21 -07:00
}
catch (Exception ex)
2018-09-12 10:26:21 -07:00
{
Logger.LogError(ex, "Error deleting file {FilePath}", path);
if (retryCount <= 40)
2017-10-13 23:52:56 -07:00
{
await Task.Delay(500).ConfigureAwait(false);
await DeleteTempFiles(path, retryCount + 1).ConfigureAwait(false);
2017-10-13 23:52:56 -07:00
}
2018-09-12 10:26:21 -07:00
}
}
2017-09-28 10:02:49 -07:00
private void TrySeek(Stream stream, long offset)
2017-09-28 10:02:49 -07:00
{
if (!stream.CanSeek)
{
return;
}
2017-09-28 10:02:49 -07:00
try
{
stream.Seek(offset, SeekOrigin.End);
2017-10-05 11:10:46 -07:00
}
catch (IOException)
{
2017-09-28 10:02:49 -07:00
}
catch (ArgumentException)
{
}
catch (Exception ex)
{
2018-12-20 05:11:26 -07:00
Logger.LogError(ex, "Error seeking stream");
2017-09-28 10:02:49 -07:00
}
}
}
}