commit
9c4240cf02
|
@ -411,8 +411,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
|
||||
host = host ?? string.Empty;
|
||||
|
||||
_logger.Debug("Validating host {0}", host);
|
||||
|
||||
if (_networkManager.IsInPrivateAddressSpace(host))
|
||||
{
|
||||
hosts.Add("localhost");
|
||||
|
|
|
@ -1222,7 +1222,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
|
||||
_logger.Info("Closing live stream {0}", id);
|
||||
|
||||
await stream.Close().ConfigureAwait(false);
|
||||
stream.Close();
|
||||
_logger.Info("Live stream {0} closed successfully", id);
|
||||
}
|
||||
}
|
||||
|
@ -1286,9 +1286,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
Id = timer.Id
|
||||
};
|
||||
|
||||
if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo))
|
||||
if (!_activeRecordings.ContainsKey(timer.Id))
|
||||
{
|
||||
await RecordStream(timer, recordingEndDate, activeRecordingInfo, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
|
||||
await RecordStream(timer, recordingEndDate, activeRecordingInfo).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1397,8 +1397,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
return Path.Combine(recordPath, recordingFileName);
|
||||
}
|
||||
|
||||
private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate,
|
||||
ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
|
||||
private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo)
|
||||
{
|
||||
if (timer == null)
|
||||
{
|
||||
|
@ -1420,19 +1419,18 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
if (programInfo != null)
|
||||
{
|
||||
CopyProgramInfoToTimerInfo(programInfo, timer);
|
||||
//activeRecordingInfo.Program = programInfo;
|
||||
}
|
||||
|
||||
string seriesPath = null;
|
||||
var recordPath = GetRecordingPath(timer, out seriesPath);
|
||||
var recordingStatus = RecordingStatus.New;
|
||||
|
||||
var recorder = await GetRecorder().ConfigureAwait(false);
|
||||
|
||||
string liveStreamId = null;
|
||||
|
||||
try
|
||||
{
|
||||
var recorder = await GetRecorder().ConfigureAwait(false);
|
||||
|
||||
var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
_logger.Info("Opening recording stream from tuner provider");
|
||||
|
@ -1442,14 +1440,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
var mediaStreamInfo = liveStreamInfo.Item2;
|
||||
liveStreamId = mediaStreamInfo.Id;
|
||||
|
||||
// HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
|
||||
//await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
|
||||
recordPath = EnsureFileUnique(recordPath, timer.Id);
|
||||
|
||||
_libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
|
||||
activeRecordingInfo.Path = recordPath;
|
||||
|
||||
var duration = recordingEndDate - DateTime.UtcNow;
|
||||
|
||||
|
@ -1459,6 +1453,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
|
||||
Action onStarted = async () =>
|
||||
{
|
||||
activeRecordingInfo.Path = recordPath;
|
||||
|
||||
_activeRecordings.TryAdd(timer.Id, activeRecordingInfo);
|
||||
|
||||
timer.Status = RecordingStatus.InProgress;
|
||||
_timerProvider.AddOrUpdate(timer, false);
|
||||
|
||||
|
@ -1470,7 +1468,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
EnforceKeepUpTo(timer, seriesPath);
|
||||
};
|
||||
|
||||
await recorder.Record(liveStreamInfo.Item1 as IDirectStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false);
|
||||
await recorder.Record(liveStreamInfo.Item1 as IDirectStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
|
||||
|
||||
recordingStatus = RecordingStatus.Completed;
|
||||
_logger.Info("Recording completed: {0}", recordPath);
|
||||
|
|
|
@ -203,7 +203,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
|
||||
var uri = new Uri(GetApiUrl(info, false));
|
||||
|
||||
using (var manager = new HdHomerunManager(_socketFactory))
|
||||
using (var manager = new HdHomerunManager(_socketFactory, Logger))
|
||||
{
|
||||
// Legacy HdHomeruns are IPv4 only
|
||||
var ipInfo = _networkManager.ParseIpAddress(uri.Host);
|
||||
|
|
|
@ -22,8 +22,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
private readonly IHttpClient _httpClient;
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
|
||||
private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment)
|
||||
: base(mediaSource, environment, fileSystem, logger, appPaths)
|
||||
{
|
||||
|
@ -32,7 +30,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
OriginalStreamId = originalStreamId;
|
||||
}
|
||||
|
||||
protected override Task OpenInternal(CancellationToken openCancellationToken)
|
||||
public override async Task Open(CancellationToken openCancellationToken)
|
||||
{
|
||||
LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
|
||||
|
@ -40,11 +38,26 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
|
||||
var url = mediaSource.Path;
|
||||
|
||||
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
|
||||
|
||||
Logger.Info("Opening HDHR Live stream from {0}", url);
|
||||
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
var response = await _httpClient.SendAsync(new HttpRequestOptions
|
||||
{
|
||||
Url = url,
|
||||
CancellationToken = CancellationToken.None,
|
||||
BufferContent = false,
|
||||
|
||||
StartStreaming(url, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
|
||||
// Increase a little bit
|
||||
TimeoutMs = 30000,
|
||||
|
||||
EnableHttpCompression = false
|
||||
|
||||
}, "GET").ConfigureAwait(false);
|
||||
|
||||
Logger.Info("Opened HDHR stream from {0}", url);
|
||||
|
||||
StartStreaming(response, LiveStreamCancellationTokenSource.Token);
|
||||
|
||||
//OpenedMediaSource.Protocol = MediaProtocol.File;
|
||||
//OpenedMediaSource.Path = tempFile;
|
||||
|
@ -58,50 +71,30 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
//OpenedMediaSource.SupportsDirectPlay = false;
|
||||
//OpenedMediaSource.SupportsDirectStream = true;
|
||||
//OpenedMediaSource.SupportsTranscoding = true;
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
|
||||
//await Task.Delay(5000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public override async Task Close()
|
||||
public override void Close()
|
||||
{
|
||||
Logger.Info("Closing HDHR live stream");
|
||||
LiveStreamCancellationTokenSource.Cancel();
|
||||
|
||||
await _liveStreamTaskCompletionSource.Task.ConfigureAwait(false);
|
||||
await DeleteTempFile(TempFilePath).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private Task StartStreaming(string url, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
|
||||
private Task StartStreaming(HttpResponseInfo response, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var response = await _httpClient.SendAsync(new HttpRequestOptions
|
||||
{
|
||||
Url = url,
|
||||
CancellationToken = cancellationToken,
|
||||
BufferContent = false,
|
||||
|
||||
// Increase a little bit
|
||||
TimeoutMs = 30000,
|
||||
|
||||
EnableHttpCompression = false
|
||||
|
||||
}, "GET").ConfigureAwait(false))
|
||||
using (response)
|
||||
{
|
||||
using (var stream = response.Content)
|
||||
{
|
||||
Logger.Info("Opened HDHR stream from {0}", url);
|
||||
|
||||
Logger.Info("Beginning multicastStream.CopyUntilCancelled");
|
||||
Logger.Info("Beginning HdHomerunHttpStream stream to file");
|
||||
|
||||
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
|
||||
using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
|
||||
{
|
||||
StreamHelper.CopyTo(stream, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken);
|
||||
StreamHelper.CopyTo(stream, fileStream, 81920, null, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +107,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
Logger.ErrorException("Error copying live stream.", ex);
|
||||
}
|
||||
|
||||
_liveStreamTaskCompletionSource.TrySetResult(true);
|
||||
await DeleteTempFile(TempFilePath).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Text.RegularExpressions;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
{
|
||||
|
@ -89,9 +90,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
private readonly ISocketFactory _socketFactory;
|
||||
private IpAddressInfo _remoteIp;
|
||||
|
||||
public HdHomerunManager(ISocketFactory socketFactory)
|
||||
private ILogger _logger;
|
||||
|
||||
public HdHomerunManager(ISocketFactory socketFactory, ILogger logger)
|
||||
{
|
||||
_socketFactory = socketFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -140,6 +144,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
_lockkey = (uint)rand.Next();
|
||||
}
|
||||
|
||||
var lockKeyValue = _lockkey.Value;
|
||||
|
||||
var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort);
|
||||
|
||||
for (int i = 0; i < numTuners; ++i)
|
||||
|
@ -148,7 +154,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
continue;
|
||||
|
||||
_activeTuner = i;
|
||||
var lockKeyString = String.Format("{0:d}", _lockkey.Value);
|
||||
var lockKeyString = String.Format("{0:d}", lockKeyValue);
|
||||
var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null);
|
||||
await tcpClient.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
|
||||
var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
|
||||
|
@ -160,27 +166,27 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
var commandList = commands.GetCommands();
|
||||
foreach(Tuple<string,string> command in commandList)
|
||||
{
|
||||
var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, _lockkey.Value);
|
||||
var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue);
|
||||
await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
|
||||
response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
|
||||
// parse response to make sure it worked
|
||||
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
|
||||
{
|
||||
await ReleaseLockkey(tcpClient).ConfigureAwait(false);
|
||||
await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var targetValue = String.Format("rtp://{0}:{1}", localIp, localPort);
|
||||
var targetMsg = CreateSetMessage(i, "target", targetValue, _lockkey.Value);
|
||||
var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue);
|
||||
|
||||
await tcpClient.SendToAsync(targetMsg, 0, targetMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
|
||||
response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
|
||||
// parse response to make sure it worked
|
||||
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
|
||||
{
|
||||
await ReleaseLockkey(tcpClient).ConfigureAwait(false);
|
||||
await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -201,7 +207,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
|
||||
foreach (Tuple<string, string> command in commandList)
|
||||
{
|
||||
var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey.Value);
|
||||
var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey);
|
||||
await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
|
||||
var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
|
||||
// parse response to make sure it worked
|
||||
|
@ -216,24 +222,28 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
|
||||
public async Task StopStreaming()
|
||||
{
|
||||
if (!_lockkey.HasValue)
|
||||
var lockKey = _lockkey;
|
||||
|
||||
if (!lockKey.HasValue)
|
||||
return;
|
||||
|
||||
using (var socket = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort))
|
||||
{
|
||||
await ReleaseLockkey(socket).ConfigureAwait(false);
|
||||
await ReleaseLockkey(socket, lockKey.Value).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReleaseLockkey(ISocket tcpClient)
|
||||
private async Task ReleaseLockkey(ISocket tcpClient, uint lockKeyValue)
|
||||
{
|
||||
var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", _lockkey);
|
||||
_logger.Info("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue);
|
||||
|
||||
var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
|
||||
await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
var receiveBuffer = new byte[8192];
|
||||
|
||||
await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
|
||||
var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", _lockkey);
|
||||
var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue);
|
||||
_lockkey = null;
|
||||
await tcpClient.SendToAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false);
|
||||
await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
|
||||
|
|
|
@ -1,23 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.IO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.System;
|
||||
using System.Globalization;
|
||||
using MediaBrowser.Controller.IO;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
{
|
||||
|
@ -26,7 +19,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly ISocketFactory _socketFactory;
|
||||
|
||||
private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
|
||||
private readonly IHdHomerunChannelCommands _channelCommands;
|
||||
private readonly int _numTuners;
|
||||
private readonly INetworkManager _networkManager;
|
||||
|
@ -42,7 +34,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
_numTuners = numTuners;
|
||||
}
|
||||
|
||||
protected override Task OpenInternal(CancellationToken openCancellationToken)
|
||||
public override async Task Open(CancellationToken openCancellationToken)
|
||||
{
|
||||
LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
|
||||
|
@ -51,11 +43,53 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
var uri = new Uri(mediaSource.Path);
|
||||
var localPort = _networkManager.GetRandomUnusedUdpPort();
|
||||
|
||||
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
|
||||
|
||||
Logger.Info("Opening HDHR UDP Live stream from {0}", uri.Host);
|
||||
|
||||
var remoteAddress = _networkManager.ParseIpAddress(uri.Host);
|
||||
IpAddressInfo localAddress = null;
|
||||
using (var tcpSocket = _socketFactory.CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, false))
|
||||
{
|
||||
try
|
||||
{
|
||||
tcpSocket.Connect(new IpEndPointInfo(remoteAddress, HdHomerunManager.HdHomeRunPort));
|
||||
localAddress = tcpSocket.LocalEndPoint.IpAddress;
|
||||
tcpSocket.Close();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.Error("Unable to determine local ip address for Legacy HDHomerun stream.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var udpClient = _socketFactory.CreateUdpSocket(localPort);
|
||||
var hdHomerunManager = new HdHomerunManager(_socketFactory, Logger);
|
||||
|
||||
try
|
||||
{
|
||||
// send url to start streaming
|
||||
await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
using (udpClient)
|
||||
{
|
||||
using (hdHomerunManager)
|
||||
{
|
||||
if (!(ex is OperationCanceledException))
|
||||
{
|
||||
Logger.ErrorException("Error opening live stream:", ex);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
StartStreaming(uri.Host, localPort, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
|
||||
StartStreaming(udpClient, hdHomerunManager, remoteAddress, localAddress, localPort, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
|
||||
|
||||
//OpenedMediaSource.Protocol = MediaProtocol.File;
|
||||
//OpenedMediaSource.Path = tempFile;
|
||||
|
@ -67,86 +101,47 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
//OpenedMediaSource.SupportsDirectStream = true;
|
||||
//OpenedMediaSource.SupportsTranscoding = true;
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
|
||||
//await Task.Delay(5000).ConfigureAwait(false);
|
||||
await taskCompletionSource.Task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public override Task Close()
|
||||
public override void Close()
|
||||
{
|
||||
Logger.Info("Closing HDHR UDP live stream");
|
||||
LiveStreamCancellationTokenSource.Cancel();
|
||||
|
||||
return _liveStreamTaskCompletionSource.Task;
|
||||
}
|
||||
|
||||
private Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
|
||||
private Task StartStreaming(ISocket udpClient, HdHomerunManager hdHomerunManager, IpAddressInfo remoteAddress, IpAddressInfo localAddress, int localPort, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var isFirstAttempt = true;
|
||||
using (var udpClient = _socketFactory.CreateUdpSocket(localPort))
|
||||
using (udpClient)
|
||||
{
|
||||
using (var hdHomerunManager = new HdHomerunManager(_socketFactory))
|
||||
using (hdHomerunManager)
|
||||
{
|
||||
var remoteAddress = _networkManager.ParseIpAddress(remoteIp);
|
||||
IpAddressInfo localAddress = null;
|
||||
using (var tcpSocket = _socketFactory.CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, false))
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
tcpSocket.Connect(new IpEndPointInfo(remoteAddress, HdHomerunManager.HdHomeRunPort));
|
||||
localAddress = tcpSocket.LocalEndPoint.IpAddress;
|
||||
tcpSocket.Close();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.Error("Unable to determine local ip address for Legacy HDHomerun stream.");
|
||||
return;
|
||||
}
|
||||
await CopyTo(udpClient, TempFilePath, openTaskCompletionSource, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
Logger.Info("HDHR UDP stream cancelled or timed out from {0}", remoteAddress);
|
||||
openTaskCompletionSource.TrySetException(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error opening live stream:", ex);
|
||||
openTaskCompletionSource.TrySetException(ex);
|
||||
}
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
// send url to start streaming
|
||||
await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Logger.Info("Opened HDHR UDP stream from {0}", remoteAddress);
|
||||
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
|
||||
using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
|
||||
{
|
||||
CopyTo(udpClient, fileStream, openTaskCompletionSource, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
Logger.Info("HDHR UDP stream cancelled or timed out from {0}", remoteAddress);
|
||||
openTaskCompletionSource.TrySetException(ex);
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (isFirstAttempt)
|
||||
{
|
||||
Logger.ErrorException("Error opening live stream:", ex);
|
||||
openTaskCompletionSource.TrySetException(ex);
|
||||
break;
|
||||
}
|
||||
|
||||
Logger.ErrorException("Error copying live stream, will reopen", ex);
|
||||
}
|
||||
|
||||
isFirstAttempt = false;
|
||||
await hdHomerunManager.StopStreaming().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
await hdHomerunManager.StopStreaming().ConfigureAwait(false);
|
||||
_liveStreamTaskCompletionSource.TrySetResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,36 +152,45 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
private void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
openTaskCompletionSource.TrySetResult(true);
|
||||
});
|
||||
{
|
||||
openTaskCompletionSource.TrySetResult(true);
|
||||
});
|
||||
}
|
||||
|
||||
private static int RtpHeaderBytes = 12;
|
||||
private void CopyTo(ISocket udpClient, Stream target, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
|
||||
private async Task CopyTo(ISocket udpClient, string file, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
|
||||
{
|
||||
var source = _socketFactory.CreateNetworkStream(udpClient, false);
|
||||
var bufferSize = 81920;
|
||||
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
int read;
|
||||
var resolved = false;
|
||||
|
||||
while ((read = source.Read(buffer, 0, buffer.Length)) != 0)
|
||||
using (var source = _socketFactory.CreateNetworkStream(udpClient, false))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
read -= RtpHeaderBytes;
|
||||
|
||||
if (read > 0)
|
||||
using (var fileStream = FileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
|
||||
{
|
||||
target.Write(buffer, RtpHeaderBytes, read);
|
||||
}
|
||||
var currentCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token).Token;
|
||||
|
||||
if (!resolved)
|
||||
{
|
||||
resolved = true;
|
||||
Resolve(openTaskCompletionSource);
|
||||
while ((read = await source.ReadAsync(buffer, 0, buffer.Length, currentCancellationToken).ConfigureAwait(false)) != 0)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
currentCancellationToken = cancellationToken;
|
||||
|
||||
read -= RtpHeaderBytes;
|
||||
|
||||
if (read > 0)
|
||||
{
|
||||
fileStream.Write(buffer, RtpHeaderBytes, read);
|
||||
}
|
||||
|
||||
if (!resolved)
|
||||
{
|
||||
resolved = true;
|
||||
Resolve(openTaskCompletionSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,19 +47,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||
TempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts");
|
||||
}
|
||||
|
||||
public Task Open(CancellationToken cancellationToken)
|
||||
{
|
||||
return OpenInternal(cancellationToken);
|
||||
}
|
||||
|
||||
protected virtual Task OpenInternal(CancellationToken cancellationToken)
|
||||
public virtual Task Open(CancellationToken openCancellationToken)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public virtual Task Close()
|
||||
public virtual void Close()
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
protected Stream GetInputStream(string path, bool allowAsyncFileRead)
|
||||
|
@ -76,6 +70,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||
|
||||
protected async Task DeleteTempFile(string path, int retryCount = 0)
|
||||
{
|
||||
if (retryCount == 0)
|
||||
{
|
||||
Logger.Info("Deleting temp file {0}", path);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
FileSystem.DeleteFile(path);
|
||||
|
|
|
@ -59,8 +59,8 @@ namespace MediaBrowser.Controller.LiveTv
|
|||
|
||||
public interface ILiveStream
|
||||
{
|
||||
Task Open(CancellationToken cancellationToken);
|
||||
Task Close();
|
||||
Task Open(CancellationToken openCancellationToken);
|
||||
void Close();
|
||||
int ConsumerCount { get; }
|
||||
string OriginalStreamId { get; set; }
|
||||
bool EnableStreamSharing { get; set; }
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("3.2.33.19")]
|
||||
[assembly: AssemblyVersion("3.2.33.20")]
|
||||
|
|
Loading…
Reference in New Issue
Block a user