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