LiveStream.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #nullable disable
  2. #pragma warning disable CS1591
  3. using System;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using MediaBrowser.Common.Configuration;
  9. using MediaBrowser.Controller.Library;
  10. using MediaBrowser.Model.Dto;
  11. using MediaBrowser.Model.IO;
  12. using MediaBrowser.Model.LiveTv;
  13. using Microsoft.Extensions.Logging;
  14. namespace Emby.Server.Implementations.LiveTv.TunerHosts
  15. {
  16. public class LiveStream : ILiveStream
  17. {
  18. private readonly IConfigurationManager _configurationManager;
  19. public LiveStream(
  20. MediaSourceInfo mediaSource,
  21. TunerHostInfo tuner,
  22. IFileSystem fileSystem,
  23. ILogger logger,
  24. IConfigurationManager configurationManager,
  25. IStreamHelper streamHelper)
  26. {
  27. OriginalMediaSource = mediaSource;
  28. FileSystem = fileSystem;
  29. MediaSource = mediaSource;
  30. Logger = logger;
  31. EnableStreamSharing = true;
  32. UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
  33. if (tuner != null)
  34. {
  35. TunerHostId = tuner.Id;
  36. }
  37. _configurationManager = configurationManager;
  38. StreamHelper = streamHelper;
  39. ConsumerCount = 1;
  40. SetTempFilePath("ts");
  41. }
  42. protected IFileSystem FileSystem { get; }
  43. protected IStreamHelper StreamHelper { get; }
  44. protected ILogger Logger { get; }
  45. protected CancellationTokenSource LiveStreamCancellationTokenSource { get; } = new CancellationTokenSource();
  46. protected string TempFilePath { get; set; }
  47. public MediaSourceInfo OriginalMediaSource { get; set; }
  48. public MediaSourceInfo MediaSource { get; set; }
  49. public int ConsumerCount { get; set; }
  50. public string OriginalStreamId { get; set; }
  51. public bool EnableStreamSharing { get; set; }
  52. public string UniqueId { get; }
  53. public string TunerHostId { get; }
  54. public DateTime DateOpened { get; protected set; }
  55. protected void SetTempFilePath(string extension)
  56. {
  57. TempFilePath = Path.Combine(_configurationManager.GetTranscodePath(), UniqueId + "." + extension);
  58. }
  59. public virtual Task Open(CancellationToken openCancellationToken)
  60. {
  61. DateOpened = DateTime.UtcNow;
  62. return Task.CompletedTask;
  63. }
  64. public Task Close()
  65. {
  66. EnableStreamSharing = false;
  67. Logger.LogInformation("Closing {Type}", GetType().Name);
  68. LiveStreamCancellationTokenSource.Cancel();
  69. return Task.CompletedTask;
  70. }
  71. public Stream GetStream()
  72. {
  73. var stream = GetInputStream(TempFilePath);
  74. bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10;
  75. if (seekFile)
  76. {
  77. TrySeek(stream, -20000);
  78. }
  79. return stream;
  80. }
  81. protected FileStream GetInputStream(string path)
  82. => new FileStream(
  83. path,
  84. FileMode.Open,
  85. FileAccess.Read,
  86. FileShare.ReadWrite,
  87. IODefaults.FileStreamBufferSize,
  88. FileOptions.SequentialScan | FileOptions.Asynchronous);
  89. protected async Task DeleteTempFiles(string path, int retryCount = 0)
  90. {
  91. if (retryCount == 0)
  92. {
  93. Logger.LogInformation("Deleting temp file {FilePath}", path);
  94. }
  95. try
  96. {
  97. FileSystem.DeleteFile(path);
  98. }
  99. catch (Exception ex)
  100. {
  101. Logger.LogError(ex, "Error deleting file {FilePath}", path);
  102. if (retryCount <= 40)
  103. {
  104. await Task.Delay(500).ConfigureAwait(false);
  105. await DeleteTempFiles(path, retryCount + 1).ConfigureAwait(false);
  106. }
  107. }
  108. }
  109. private void TrySeek(Stream stream, long offset)
  110. {
  111. if (!stream.CanSeek)
  112. {
  113. return;
  114. }
  115. try
  116. {
  117. stream.Seek(offset, SeekOrigin.End);
  118. }
  119. catch (IOException)
  120. {
  121. }
  122. catch (ArgumentException)
  123. {
  124. }
  125. catch (Exception ex)
  126. {
  127. Logger.LogError(ex, "Error seeking stream");
  128. }
  129. }
  130. }
  131. }