2021-05-20 12:28:18 -07:00
|
|
|
#nullable disable
|
|
|
|
|
2020-02-06 07:20:23 -07:00
|
|
|
#pragma warning disable CS1591
|
|
|
|
|
2019-01-13 12:54:44 -07:00
|
|
|
using System;
|
2019-02-28 15:22:57 -07:00
|
|
|
using System.Globalization;
|
2017-09-28 10:02:49 -07:00
|
|
|
using System.IO;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
2019-11-08 04:49:00 -07:00
|
|
|
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;
|
2017-11-17 14:54:33 -07:00
|
|
|
using MediaBrowser.Model.LiveTv;
|
2019-01-13 12:22:00 -07:00
|
|
|
using Microsoft.Extensions.Logging;
|
2017-09-28 10:02:49 -07:00
|
|
|
|
|
|
|
namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|
|
|
{
|
|
|
|
public class LiveStream : ILiveStream
|
|
|
|
{
|
2019-11-08 04:49:00 -07:00
|
|
|
private readonly IConfigurationManager _configurationManager;
|
|
|
|
|
2017-09-28 10:02:49 -07:00
|
|
|
protected readonly IFileSystem FileSystem;
|
2019-11-08 04:49:00 -07:00
|
|
|
|
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,
|
2019-11-08 04:49:00 -07:00
|
|
|
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;
|
2019-02-28 15:22:57 -07:00
|
|
|
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
|
|
|
|
2019-11-08 04:49:00 -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)
|
|
|
|
{
|
2019-11-08 04:49:00 -07:00
|
|
|
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()
|
2017-11-05 14:51:23 -07:00
|
|
|
{
|
|
|
|
EnableStreamSharing = false;
|
|
|
|
|
2019-07-07 07:39:35 -07:00
|
|
|
Logger.LogInformation("Closing {Type}", GetType().Name);
|
2017-11-05 14:51:23 -07:00
|
|
|
|
2018-09-12 10:26:21 -07:00
|
|
|
LiveStreamCancellationTokenSource.Cancel();
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
2017-11-05 14:51:23 -07:00
|
|
|
}
|
|
|
|
|
2021-09-10 00:56:48 -07:00
|
|
|
public Stream GetStream()
|
2021-09-10 00:29:14 -07:00
|
|
|
{
|
|
|
|
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
|
|
|
|
2021-09-10 00:29:14 -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)
|
|
|
|
{
|
2021-09-10 00:29:14 -07:00
|
|
|
Logger.LogInformation("Deleting temp file {FilePath}", path);
|
2017-09-28 10:02:49 -07:00
|
|
|
}
|
|
|
|
|
2021-09-10 00:29:14 -07:00
|
|
|
try
|
2017-09-28 10:02:49 -07:00
|
|
|
{
|
2021-09-10 00:29:14 -07:00
|
|
|
FileSystem.DeleteFile(path);
|
2018-09-12 10:26:21 -07:00
|
|
|
}
|
2021-09-10 00:29:14 -07:00
|
|
|
catch (Exception ex)
|
2018-09-12 10:26:21 -07:00
|
|
|
{
|
2021-09-10 00:29:14 -07:00
|
|
|
Logger.LogError(ex, "Error deleting file {FilePath}", path);
|
|
|
|
if (retryCount <= 40)
|
2017-10-13 23:52:56 -07:00
|
|
|
{
|
2021-09-10 00:29:14 -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
|
|
|
|
2021-09-10 00:29:14 -07:00
|
|
|
private void TrySeek(Stream stream, long offset)
|
2017-09-28 10:02:49 -07:00
|
|
|
{
|
2019-03-13 09:51:33 -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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|