DirectRecorder.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. using System;
  2. using System.IO;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using MediaBrowser.Model.IO;
  6. using MediaBrowser.Common.Net;
  7. using MediaBrowser.Controller.IO;
  8. using MediaBrowser.Controller.Library;
  9. using MediaBrowser.Model.Dto;
  10. using MediaBrowser.Model.Logging;
  11. namespace Emby.Server.Implementations.LiveTv.EmbyTV
  12. {
  13. public class DirectRecorder : IRecorder
  14. {
  15. private readonly ILogger _logger;
  16. private readonly IHttpClient _httpClient;
  17. private readonly IFileSystem _fileSystem;
  18. public DirectRecorder(ILogger logger, IHttpClient httpClient, IFileSystem fileSystem)
  19. {
  20. _logger = logger;
  21. _httpClient = httpClient;
  22. _fileSystem = fileSystem;
  23. }
  24. public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile)
  25. {
  26. return targetFile;
  27. }
  28. public Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
  29. {
  30. if (directStreamProvider != null)
  31. {
  32. return RecordFromDirectStreamProvider(directStreamProvider, targetFile, duration, onStarted, cancellationToken);
  33. }
  34. return RecordFromMediaSource(mediaSource, targetFile, duration, onStarted, cancellationToken);
  35. }
  36. private async Task RecordFromDirectStreamProvider(IDirectStreamProvider directStreamProvider, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
  37. {
  38. _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(targetFile));
  39. using (var output = _fileSystem.GetFileStream(targetFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
  40. {
  41. onStarted();
  42. _logger.Info("Copying recording stream to file {0}", targetFile);
  43. // The media source if infinite so we need to handle stopping ourselves
  44. var durationToken = new CancellationTokenSource(duration);
  45. cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
  46. await directStreamProvider.CopyToAsync(output, cancellationToken).ConfigureAwait(false);
  47. }
  48. _logger.Info("Recording completed to file {0}", targetFile);
  49. }
  50. private async Task RecordFromMediaSource(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
  51. {
  52. var httpRequestOptions = new HttpRequestOptions
  53. {
  54. Url = mediaSource.Path,
  55. BufferContent = false,
  56. // Some remote urls will expect a user agent to be supplied
  57. UserAgent = "Emby/3.0",
  58. // Shouldn't matter but may cause issues
  59. EnableHttpCompression = false
  60. };
  61. using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false))
  62. {
  63. _logger.Info("Opened recording stream from tuner provider");
  64. _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(targetFile));
  65. using (var output = _fileSystem.GetFileStream(targetFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
  66. {
  67. onStarted();
  68. _logger.Info("Copying recording stream to file {0}", targetFile);
  69. // The media source if infinite so we need to handle stopping ourselves
  70. var durationToken = new CancellationTokenSource(duration);
  71. cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
  72. await CopyUntilCancelled(response.Content, output, cancellationToken).ConfigureAwait(false);
  73. }
  74. }
  75. _logger.Info("Recording completed to file {0}", targetFile);
  76. }
  77. private const int BufferSize = 81920;
  78. public static async Task CopyUntilCancelled(Stream source, Stream target, CancellationToken cancellationToken)
  79. {
  80. byte[] buffer = new byte[BufferSize];
  81. while (!cancellationToken.IsCancellationRequested)
  82. {
  83. var bytesRead = await CopyToAsyncInternal(source, target, buffer, cancellationToken).ConfigureAwait(false);
  84. //var position = fs.Position;
  85. //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
  86. if (bytesRead == 0)
  87. {
  88. await Task.Delay(100).ConfigureAwait(false);
  89. }
  90. }
  91. }
  92. private static async Task<int> CopyToAsyncInternal(Stream source, Stream destination, byte[] buffer, CancellationToken cancellationToken)
  93. {
  94. int bytesRead;
  95. int totalBytesRead = 0;
  96. while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
  97. {
  98. destination.Write(buffer, 0, bytesRead);
  99. totalBytesRead += bytesRead;
  100. }
  101. return totalBytesRead;
  102. }
  103. }
  104. }