HdHomerunUdpStream.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. using System;
  2. using System.Buffers;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using MediaBrowser.Common.Net;
  10. using MediaBrowser.Controller;
  11. using MediaBrowser.Controller.Library;
  12. using MediaBrowser.Model.Dto;
  13. using MediaBrowser.Model.IO;
  14. using MediaBrowser.Model.LiveTv;
  15. using MediaBrowser.Model.MediaInfo;
  16. using Microsoft.Extensions.Logging;
  17. namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
  18. {
  19. public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider
  20. {
  21. private const int RtpHeaderBytes = 12;
  22. private readonly IServerApplicationHost _appHost;
  23. private readonly MediaBrowser.Model.Net.ISocketFactory _socketFactory;
  24. private readonly IHdHomerunChannelCommands _channelCommands;
  25. private readonly int _numTuners;
  26. private readonly INetworkManager _networkManager;
  27. public HdHomerunUdpStream(
  28. MediaSourceInfo mediaSource,
  29. TunerHostInfo tunerHostInfo,
  30. string originalStreamId,
  31. IHdHomerunChannelCommands channelCommands,
  32. int numTuners,
  33. IFileSystem fileSystem,
  34. ILogger logger,
  35. IServerApplicationPaths appPaths,
  36. IServerApplicationHost appHost,
  37. MediaBrowser.Model.Net.ISocketFactory socketFactory,
  38. INetworkManager networkManager,
  39. IStreamHelper streamHelper)
  40. : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths, streamHelper)
  41. {
  42. _appHost = appHost;
  43. _socketFactory = socketFactory;
  44. _networkManager = networkManager;
  45. OriginalStreamId = originalStreamId;
  46. _channelCommands = channelCommands;
  47. _numTuners = numTuners;
  48. EnableStreamSharing = true;
  49. }
  50. public override async Task Open(CancellationToken openCancellationToken)
  51. {
  52. LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
  53. var mediaSource = OriginalMediaSource;
  54. var uri = new Uri(mediaSource.Path);
  55. var localPort = _networkManager.GetRandomUnusedUdpPort();
  56. Directory.CreateDirectory(Path.GetDirectoryName(TempFilePath));
  57. Logger.LogInformation("Opening HDHR UDP Live stream from {host}", uri.Host);
  58. var remoteAddress = IPAddress.Parse(uri.Host);
  59. IPAddress localAddress = null;
  60. using (var tcpClient = new TcpClient())
  61. {
  62. try
  63. {
  64. await tcpClient.ConnectAsync(remoteAddress, HdHomerunManager.HdHomeRunPort).ConfigureAwait(false);
  65. localAddress = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address;
  66. tcpClient.Close();
  67. }
  68. catch (Exception ex)
  69. {
  70. Logger.LogError(ex, "Unable to determine local ip address for Legacy HDHomerun stream.");
  71. return;
  72. }
  73. }
  74. var udpClient = _socketFactory.CreateUdpSocket(localPort);
  75. var hdHomerunManager = new HdHomerunManager();
  76. try
  77. {
  78. // send url to start streaming
  79. await hdHomerunManager.StartStreaming(
  80. remoteAddress,
  81. localAddress,
  82. localPort,
  83. _channelCommands,
  84. _numTuners,
  85. openCancellationToken).ConfigureAwait(false);
  86. }
  87. catch (Exception ex)
  88. {
  89. using (udpClient)
  90. using (hdHomerunManager)
  91. {
  92. if (!(ex is OperationCanceledException))
  93. {
  94. Logger.LogError(ex, "Error opening live stream:");
  95. }
  96. throw;
  97. }
  98. }
  99. var taskCompletionSource = new TaskCompletionSource<bool>();
  100. await StartStreaming(
  101. udpClient,
  102. hdHomerunManager,
  103. remoteAddress,
  104. taskCompletionSource,
  105. LiveStreamCancellationTokenSource.Token).ConfigureAwait(false);
  106. //OpenedMediaSource.Protocol = MediaProtocol.File;
  107. //OpenedMediaSource.Path = tempFile;
  108. //OpenedMediaSource.ReadAtNativeFramerate = true;
  109. MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
  110. MediaSource.Protocol = MediaProtocol.Http;
  111. //OpenedMediaSource.SupportsDirectPlay = false;
  112. //OpenedMediaSource.SupportsDirectStream = true;
  113. //OpenedMediaSource.SupportsTranscoding = true;
  114. //await Task.Delay(5000).ConfigureAwait(false);
  115. await taskCompletionSource.Task.ConfigureAwait(false);
  116. }
  117. private Task StartStreaming(MediaBrowser.Model.Net.ISocket udpClient, HdHomerunManager hdHomerunManager, IPAddress remoteAddress, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
  118. {
  119. return Task.Run(async () =>
  120. {
  121. using (udpClient)
  122. using (hdHomerunManager)
  123. {
  124. try
  125. {
  126. await CopyTo(udpClient, TempFilePath, openTaskCompletionSource, cancellationToken).ConfigureAwait(false);
  127. }
  128. catch (OperationCanceledException ex)
  129. {
  130. Logger.LogInformation("HDHR UDP stream cancelled or timed out from {0}", remoteAddress);
  131. openTaskCompletionSource.TrySetException(ex);
  132. }
  133. catch (Exception ex)
  134. {
  135. Logger.LogError(ex, "Error opening live stream:");
  136. openTaskCompletionSource.TrySetException(ex);
  137. }
  138. EnableStreamSharing = false;
  139. }
  140. await DeleteTempFiles(new List<string> { TempFilePath }).ConfigureAwait(false);
  141. });
  142. }
  143. private async Task CopyTo(MediaBrowser.Model.Net.ISocket udpClient, string file, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
  144. {
  145. byte[] buffer = ArrayPool<byte>.Shared.Rent(StreamDefaults.DefaultCopyToBufferSize);
  146. try
  147. {
  148. using (var source = _socketFactory.CreateNetworkStream(udpClient, false))
  149. using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
  150. {
  151. var currentCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token).Token;
  152. int read;
  153. var resolved = false;
  154. while ((read = await source.ReadAsync(buffer, 0, buffer.Length, currentCancellationToken).ConfigureAwait(false)) != 0)
  155. {
  156. cancellationToken.ThrowIfCancellationRequested();
  157. currentCancellationToken = cancellationToken;
  158. read -= RtpHeaderBytes;
  159. if (read > 0)
  160. {
  161. await fileStream.WriteAsync(buffer, RtpHeaderBytes, read).ConfigureAwait(false);
  162. }
  163. if (!resolved)
  164. {
  165. resolved = true;
  166. DateOpened = DateTime.UtcNow;
  167. openTaskCompletionSource.TrySetResult(true);
  168. }
  169. }
  170. }
  171. }
  172. finally
  173. {
  174. ArrayPool<byte>.Shared.Return(buffer);
  175. }
  176. }
  177. }
  178. }