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