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