mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-07-09 15:20:34 +02:00
Use System.Net abstractions instead of raw socket
This commit is contained in:
parent
779f0c637f
commit
1cad93c276
|
@ -255,7 +255,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
|
|
||||||
var uri = new Uri(GetApiUrl(info));
|
var uri = new Uri(GetApiUrl(info));
|
||||||
|
|
||||||
using (var manager = new HdHomerunManager(_socketFactory, Logger))
|
using (var manager = new HdHomerunManager(Logger))
|
||||||
{
|
{
|
||||||
// Legacy HdHomeruns are IPv4 only
|
// Legacy HdHomeruns are IPv4 only
|
||||||
var ipInfo = IPAddress.Parse(uri.Host);
|
var ipInfo = IPAddress.Parse(uri.Host);
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Model.Net;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
|
@ -78,37 +79,36 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
|
|
||||||
public class HdHomerunManager : IDisposable
|
public class HdHomerunManager : IDisposable
|
||||||
{
|
{
|
||||||
public static int HdHomeRunPort = 65001;
|
public const int HdHomeRunPort = 65001;
|
||||||
// Message constants
|
// Message constants
|
||||||
private static byte GetSetName = 3;
|
private const byte GetSetName = 3;
|
||||||
private static byte GetSetValue = 4;
|
private const byte GetSetValue = 4;
|
||||||
private static byte GetSetLockkey = 21;
|
private const byte GetSetLockkey = 21;
|
||||||
private static ushort GetSetRequest = 4;
|
private const ushort GetSetRequest = 4;
|
||||||
private static ushort GetSetReply = 5;
|
private const ushort GetSetReply = 5;
|
||||||
|
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private uint? _lockkey = null;
|
private uint? _lockkey = null;
|
||||||
private int _activeTuner = -1;
|
private int _activeTuner = -1;
|
||||||
private readonly ISocketFactory _socketFactory;
|
private IPEndPoint _remoteEndPoint;
|
||||||
private IPAddress _remoteIp;
|
|
||||||
|
|
||||||
private ILogger _logger;
|
private TcpClient _tcpClient;
|
||||||
private ISocket _currentTcpSocket;
|
|
||||||
|
|
||||||
public HdHomerunManager(ISocketFactory socketFactory, ILogger logger)
|
public HdHomerunManager(ILogger logger)
|
||||||
{
|
{
|
||||||
_socketFactory = socketFactory;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
using (var socket = _currentTcpSocket)
|
using (var socket = _tcpClient)
|
||||||
{
|
{
|
||||||
if (socket != null)
|
if (socket != null)
|
||||||
{
|
{
|
||||||
_currentTcpSocket = null;
|
_tcpClient = null;
|
||||||
|
|
||||||
var task = StopStreaming(socket);
|
var task = StopStreaming(_tcpClient);
|
||||||
Task.WaitAll(task);
|
Task.WaitAll(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,35 +116,38 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
|
|
||||||
public async Task<bool> CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
|
public async Task<bool> CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
using (var socket = _socketFactory.CreateTcpSocket(remoteIp, HdHomeRunPort))
|
using (var client = new TcpClient(new IPEndPoint(remoteIp, HdHomeRunPort)))
|
||||||
|
using (var stream = client.GetStream())
|
||||||
{
|
{
|
||||||
return await CheckTunerAvailability(socket, remoteIp, tuner, cancellationToken).ConfigureAwait(false);
|
return await CheckTunerAvailability(stream, tuner, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<bool> CheckTunerAvailability(ISocket socket, IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
|
private static async Task<bool> CheckTunerAvailability(NetworkStream stream, int tuner, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var ipEndPoint = new IPEndPoint(remoteIp, HdHomeRunPort);
|
|
||||||
|
|
||||||
var lockkeyMsg = CreateGetMessage(tuner, "lockkey");
|
var lockkeyMsg = CreateGetMessage(tuner, "lockkey");
|
||||||
await socket.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken);
|
await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var receiveBuffer = new byte[8192];
|
byte[] buffer = ArrayPool<byte>.Shared.Rent(8192);
|
||||||
var response = await socket.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
|
try
|
||||||
|
{
|
||||||
|
int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
ParseReturnMessage(response.Buffer, response.ReceivedBytes, out string returnVal);
|
ParseReturnMessage(buffer, receivedBytes, out string returnVal);
|
||||||
|
|
||||||
return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase);
|
return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartStreaming(IPAddress remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken)
|
public async Task StartStreaming(IPAddress remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_remoteIp = remoteIp;
|
_remoteEndPoint = new IPEndPoint(remoteIp, HdHomeRunPort);
|
||||||
|
|
||||||
var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort);
|
_tcpClient = new TcpClient(_remoteEndPoint);
|
||||||
_currentTcpSocket = tcpClient;
|
|
||||||
|
|
||||||
var receiveBuffer = new byte[8192];
|
|
||||||
|
|
||||||
if (!_lockkey.HasValue)
|
if (!_lockkey.HasValue)
|
||||||
{
|
{
|
||||||
|
@ -153,51 +156,61 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
}
|
}
|
||||||
|
|
||||||
var lockKeyValue = _lockkey.Value;
|
var lockKeyValue = _lockkey.Value;
|
||||||
|
var stream = _tcpClient.GetStream();
|
||||||
|
|
||||||
var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort);
|
byte[] buffer = ArrayPool<byte>.Shared.Rent(8192);
|
||||||
|
try
|
||||||
for (int i = 0; i < numTuners; ++i)
|
|
||||||
{
|
{
|
||||||
if (!await CheckTunerAvailability(tcpClient, _remoteIp, i, cancellationToken).ConfigureAwait(false))
|
for (int i = 0; i < numTuners; ++i)
|
||||||
continue;
|
|
||||||
|
|
||||||
_activeTuner = i;
|
|
||||||
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);
|
|
||||||
// parse response to make sure it worked
|
|
||||||
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out var returnVal))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var commandList = commands.GetCommands();
|
|
||||||
foreach (Tuple<string, string> command in commandList)
|
|
||||||
{
|
{
|
||||||
var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue);
|
if (!await CheckTunerAvailability(stream, i, cancellationToken).ConfigureAwait(false))
|
||||||
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, lockKeyValue).ConfigureAwait(false);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_activeTuner = i;
|
||||||
|
var lockKeyString = string.Format("{0:d}", lockKeyValue);
|
||||||
|
var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null);
|
||||||
|
await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false);
|
||||||
|
int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
|
||||||
|
// parse response to make sure it worked
|
||||||
|
if (!ParseReturnMessage(buffer, receivedBytes, out var returnVal))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandList = commands.GetCommands();
|
||||||
|
foreach (Tuple<string, string> command in commandList)
|
||||||
|
{
|
||||||
|
var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue);
|
||||||
|
await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false);
|
||||||
|
receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
|
||||||
|
// parse response to make sure it worked
|
||||||
|
if (!ParseReturnMessage(buffer, receivedBytes, out returnVal))
|
||||||
|
{
|
||||||
|
await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetValue = string.Format("rtp://{0}:{1}", localIp, localPort);
|
||||||
|
var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue);
|
||||||
|
|
||||||
|
await stream.WriteAsync(targetMsg, 0, targetMsg.Length, cancellationToken).ConfigureAwait(false);
|
||||||
|
receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
|
||||||
|
// parse response to make sure it worked
|
||||||
|
if (!ParseReturnMessage(buffer, receivedBytes, out returnVal))
|
||||||
|
{
|
||||||
|
await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
var targetValue = string.Format("rtp://{0}:{1}", localIp, localPort);
|
finally
|
||||||
var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue);
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(buffer);
|
||||||
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, lockKeyValue).ConfigureAwait(false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_activeTuner = -1;
|
_activeTuner = -1;
|
||||||
|
@ -207,53 +220,70 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
public async Task ChangeChannel(IHdHomerunChannelCommands commands, CancellationToken cancellationToken)
|
public async Task ChangeChannel(IHdHomerunChannelCommands commands, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!_lockkey.HasValue)
|
if (!_lockkey.HasValue)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
using (var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort))
|
using (var tcpClient = new TcpClient(_remoteEndPoint))
|
||||||
|
using (var stream = tcpClient.GetStream())
|
||||||
{
|
{
|
||||||
var commandList = commands.GetCommands();
|
var commandList = commands.GetCommands();
|
||||||
var receiveBuffer = new byte[8192];
|
byte[] buffer = ArrayPool<byte>.Shared.Rent(8192);
|
||||||
|
try
|
||||||
foreach (Tuple<string, string> command in commandList)
|
|
||||||
{
|
{
|
||||||
var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey);
|
foreach (Tuple<string, string> command in commandList)
|
||||||
await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IPEndPoint(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
|
|
||||||
var 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 string returnVal))
|
|
||||||
{
|
{
|
||||||
return;
|
var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey);
|
||||||
|
await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false);
|
||||||
|
int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
|
||||||
|
// parse response to make sure it worked
|
||||||
|
if (!ParseReturnMessage(buffer, receivedBytes, out string returnVal))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopStreaming(ISocket socket)
|
public Task StopStreaming(TcpClient client)
|
||||||
{
|
{
|
||||||
var lockKey = _lockkey;
|
var lockKey = _lockkey;
|
||||||
|
|
||||||
if (!lockKey.HasValue)
|
if (!lockKey.HasValue)
|
||||||
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
return ReleaseLockkey(socket, lockKey.Value);
|
return ReleaseLockkey(client, lockKey.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReleaseLockkey(ISocket tcpClient, uint lockKeyValue)
|
private async Task ReleaseLockkey(TcpClient client, uint lockKeyValue)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue);
|
_logger.LogInformation("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue);
|
||||||
|
|
||||||
var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort);
|
var stream = client.GetStream();
|
||||||
|
|
||||||
var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
|
var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
|
||||||
await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false);
|
await stream.WriteAsync(releaseTarget, 0, releaseTarget.Length, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
var receiveBuffer = new byte[8192];
|
var buffer = ArrayPool<byte>.Shared.Rent(8192);
|
||||||
|
try
|
||||||
await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
|
{
|
||||||
var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue);
|
await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false);
|
||||||
_lockkey = null;
|
var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue);
|
||||||
await tcpClient.SendToAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false);
|
_lockkey = null;
|
||||||
await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
|
await stream.WriteAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] CreateGetMessage(int tuner, string name)
|
private static byte[] CreateGetMessage(int tuner, string name)
|
||||||
|
@ -270,7 +300,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
// calculate crc and insert at the end of the message
|
// calculate crc and insert at the end of the message
|
||||||
var crcBytes = BitConverter.GetBytes(HdHomerunCrc.GetCrc32(message, messageLength - 4));
|
var crcBytes = BitConverter.GetBytes(HdHomerunCrc.GetCrc32(message, messageLength - 4));
|
||||||
if (flipEndian)
|
if (flipEndian)
|
||||||
|
{
|
||||||
Array.Reverse(crcBytes);
|
Array.Reverse(crcBytes);
|
||||||
|
}
|
||||||
|
|
||||||
Buffer.BlockCopy(crcBytes, 0, message, offset, 4);
|
Buffer.BlockCopy(crcBytes, 0, message, offset, 4);
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
|
|
|
@ -49,13 +49,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
EnableStreamSharing = true;
|
EnableStreamSharing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Socket CreateSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
|
|
||||||
{
|
|
||||||
var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task Open(CancellationToken openCancellationToken)
|
public override async Task Open(CancellationToken openCancellationToken)
|
||||||
{
|
{
|
||||||
LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||||
|
@ -71,13 +64,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
|
|
||||||
var remoteAddress = IPAddress.Parse(uri.Host);
|
var remoteAddress = IPAddress.Parse(uri.Host);
|
||||||
IPAddress localAddress = null;
|
IPAddress localAddress = null;
|
||||||
using (var tcpSocket = CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
|
using (var tcpClient = new TcpClient())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tcpSocket.Connect(new IPEndPoint(remoteAddress, HdHomerunManager.HdHomeRunPort));
|
await tcpClient.ConnectAsync(remoteAddress, HdHomerunManager.HdHomeRunPort).ConfigureAwait(false);
|
||||||
localAddress = ((IPEndPoint)tcpSocket.LocalEndPoint).Address;
|
localAddress = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address;
|
||||||
tcpSocket.Close();
|
tcpClient.Close();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -87,7 +80,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
}
|
}
|
||||||
|
|
||||||
var udpClient = _socketFactory.CreateUdpSocket(localPort);
|
var udpClient = _socketFactory.CreateUdpSocket(localPort);
|
||||||
var hdHomerunManager = new HdHomerunManager(_socketFactory, Logger);
|
var hdHomerunManager = new HdHomerunManager(Logger);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -103,6 +96,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
{
|
{
|
||||||
Logger.LogError(ex, "Error opening live stream:");
|
Logger.LogError(ex, "Error opening live stream:");
|
||||||
}
|
}
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,126 +193,5 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UdpClientStream : Stream
|
|
||||||
{
|
|
||||||
private static int RtpHeaderBytes = 12;
|
|
||||||
private static int PacketSize = 1316;
|
|
||||||
private readonly MediaBrowser.Model.Net.ISocket _udpClient;
|
|
||||||
bool disposed;
|
|
||||||
|
|
||||||
public UdpClientStream(MediaBrowser.Model.Net.ISocket udpClient) : base()
|
|
||||||
{
|
|
||||||
_udpClient = udpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (buffer == null)
|
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
|
||||||
|
|
||||||
if (offset + count < 0)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(offset), "offset + count must not be negative");
|
|
||||||
|
|
||||||
if (offset + count > buffer.Length)
|
|
||||||
throw new ArgumentException("offset + count must not be greater than the length of buffer");
|
|
||||||
|
|
||||||
if (disposed)
|
|
||||||
throw new ObjectDisposedException(nameof(UdpClientStream));
|
|
||||||
|
|
||||||
// This will always receive a 1328 packet size (PacketSize + RtpHeaderSize)
|
|
||||||
// The RTP header will be stripped so see how many reads we need to make to fill the buffer.
|
|
||||||
int numReads = count / PacketSize;
|
|
||||||
int totalBytesRead = 0;
|
|
||||||
byte[] receiveBuffer = new byte[81920];
|
|
||||||
|
|
||||||
for (int i = 0; i < numReads; ++i)
|
|
||||||
{
|
|
||||||
var data = await _udpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var bytesRead = data.ReceivedBytes - RtpHeaderBytes;
|
|
||||||
|
|
||||||
// remove rtp header
|
|
||||||
Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead);
|
|
||||||
offset += bytesRead;
|
|
||||||
totalBytesRead += bytesRead;
|
|
||||||
}
|
|
||||||
return totalBytesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Read(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (buffer == null)
|
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
|
||||||
|
|
||||||
if (offset + count < 0)
|
|
||||||
throw new ArgumentOutOfRangeException("offset + count must not be negative", "offset+count");
|
|
||||||
|
|
||||||
if (offset + count > buffer.Length)
|
|
||||||
throw new ArgumentException("offset + count must not be greater than the length of buffer");
|
|
||||||
|
|
||||||
if (disposed)
|
|
||||||
throw new ObjectDisposedException(nameof(UdpClientStream));
|
|
||||||
|
|
||||||
// This will always receive a 1328 packet size (PacketSize + RtpHeaderSize)
|
|
||||||
// The RTP header will be stripped so see how many reads we need to make to fill the buffer.
|
|
||||||
int numReads = count / PacketSize;
|
|
||||||
int totalBytesRead = 0;
|
|
||||||
byte[] receiveBuffer = new byte[81920];
|
|
||||||
|
|
||||||
for (int i = 0; i < numReads; ++i)
|
|
||||||
{
|
|
||||||
var receivedBytes = _udpClient.Receive(receiveBuffer, 0, receiveBuffer.Length);
|
|
||||||
|
|
||||||
var bytesRead = receivedBytes - RtpHeaderBytes;
|
|
||||||
|
|
||||||
// remove rtp header
|
|
||||||
Buffer.BlockCopy(receiveBuffer, RtpHeaderBytes, buffer, offset, bytesRead);
|
|
||||||
offset += bytesRead;
|
|
||||||
totalBytesRead += bytesRead;
|
|
||||||
}
|
|
||||||
return totalBytesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
disposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanRead => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public override bool CanSeek => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public override bool CanWrite => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public override long Length => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public override long Position
|
|
||||||
{
|
|
||||||
get => throw new NotImplementedException();
|
|
||||||
|
|
||||||
set => throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength(long value)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,54 +2,12 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using Emby.Server.Implementations.Networking;
|
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Net
|
namespace Emby.Server.Implementations.Net
|
||||||
{
|
{
|
||||||
public class SocketFactory : ISocketFactory
|
public class SocketFactory : ISocketFactory
|
||||||
{
|
{
|
||||||
// THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
|
|
||||||
// Be careful to check any changes compile and work for all platform projects it is shared in.
|
|
||||||
|
|
||||||
// Not entirely happy with this. Would have liked to have done something more generic/reusable,
|
|
||||||
// but that wasn't really the point so kept to YAGNI principal for now, even if the
|
|
||||||
// interfaces are a bit ugly, specific and make assumptions.
|
|
||||||
|
|
||||||
public ISocket CreateTcpSocket(IPAddress remoteAddress, int remotePort)
|
|
||||||
{
|
|
||||||
if (remotePort < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("remotePort cannot be less than zero.", nameof(remotePort));
|
|
||||||
}
|
|
||||||
|
|
||||||
var addressFamily = remoteAddress.AddressFamily == AddressFamily.InterNetwork
|
|
||||||
? AddressFamily.InterNetwork
|
|
||||||
: AddressFamily.InterNetworkV6;
|
|
||||||
|
|
||||||
var retVal = new Socket(addressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
|
||||||
}
|
|
||||||
catch (SocketException)
|
|
||||||
{
|
|
||||||
// This is not supported on all operating systems (qnap)
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return new UdpSocket(retVal, new IPEndPoint(remoteAddress, remotePort));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
retVal?.Dispose();
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new UDP acceptSocket and binds it to the specified local port.
|
/// Creates a new UDP acceptSocket and binds it to the specified local port.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -17,8 +17,6 @@ namespace MediaBrowser.Model.Net
|
||||||
|
|
||||||
ISocket CreateUdpBroadcastSocket(int localPort);
|
ISocket CreateUdpBroadcastSocket(int localPort);
|
||||||
|
|
||||||
ISocket CreateTcpSocket(IPAddress remoteAddress, int remotePort);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new unicast socket using the specified local port number.
|
/// Creates a new unicast socket using the specified local port number.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -35,14 +33,4 @@ namespace MediaBrowser.Model.Net
|
||||||
|
|
||||||
Stream CreateNetworkStream(ISocket socket, bool ownsSocket);
|
Stream CreateNetworkStream(ISocket socket, bool ownsSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SocketType
|
|
||||||
{
|
|
||||||
Stream
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ProtocolType
|
|
||||||
{
|
|
||||||
Tcp
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue