Skip to content

Commit

Permalink
Make network bind interface selectable for the receiver (Closes #11)
Browse files Browse the repository at this point in the history
  • Loading branch information
YorVeX committed Jun 10, 2023
1 parent 2f4ebb2 commit 09f45cf
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 8 deletions.
8 changes: 5 additions & 3 deletions BeamReceiver.cs
Expand Up @@ -5,6 +5,7 @@
using System.Buffers.Binary;
using System.IO.Pipelines;
using System.IO.Pipes;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -51,12 +52,12 @@ public uint Height

public int FrameBufferTimeMs { get; set; }

public void Connect(string hostname, int port)
public void Connect(IPAddress bindAddress, string hostname, int port)
{
Task.Run(() => ConnectAsync(hostname, port));
Task.Run(() => ConnectAsync(bindAddress, hostname, port));
}

public async Task ConnectAsync(string hostname, int port)
public async Task ConnectAsync(IPAddress bindAddress, string hostname, int port)
{
if (_isConnecting || IsConnected)
return;
Expand All @@ -76,6 +77,7 @@ public async Task ConnectAsync(string hostname, int port)
try
{
Module.Log($"Connecting to {_targetHostname}:{_targetPort}...", ObsLogLevel.Debug);
socket.Bind(new IPEndPoint(bindAddress, 0));
await socket.ConnectAsync(_targetHostname, _targetPort, _cancellationSource.Token);
}
catch (Exception ex)
Expand Down
74 changes: 70 additions & 4 deletions Source.cs
Expand Up @@ -2,7 +2,11 @@
// SPDX-License-Identifier: MIT

using System.Collections.Concurrent;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using ObsInterop;

namespace xObsBeam;
Expand Down Expand Up @@ -62,6 +66,39 @@ private static unsafe Source GetSource(void* data)
return _sourceList[(*context).SourceId];
}

public unsafe string NetworkInterfaceName
{
get
{
fixed (byte* propertyNetworkInterfaceListId = "network_interface_list"u8)
return Marshal.PtrToStringUTF8((IntPtr)ObsData.obs_data_get_string(((Context*)ContextPointer)->Settings, (sbyte*)propertyNetworkInterfaceListId))!;
}
}

public unsafe IPAddress NetworkInterfaceAddress
{
get
{
var configuredNetworkInterfaceName = NetworkInterfaceName;
if (configuredNetworkInterfaceName == "Any: 0.0.0.0")
return IPAddress.Any;

foreach (var networkInterface in NetworkInterface.GetAllNetworkInterfaces())
{
foreach (var ip in networkInterface.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily != AddressFamily.InterNetwork)
continue;
string networkInterfaceDisplayName = networkInterface.Name + ": " + ip.Address + " / " + ip.IPv4Mask;
if (networkInterfaceDisplayName == configuredNetworkInterfaceName)
return ip.Address;
}
}
Module.Log($"Didn't find configured network interface \"{configuredNetworkInterfaceName}\", falling back to loopback interface.", ObsLogLevel.Error);
return IPAddress.Loopback;
}
}

private unsafe void Connect()
{
var context = (Context*)ContextPointer;
Expand Down Expand Up @@ -103,7 +140,7 @@ private unsafe void Connect()
if (string.IsNullOrEmpty(targetHost) || targetHost == ".")
targetHost = Marshal.PtrToStringUTF8((IntPtr)ObsData.obs_data_get_default_string(settings, (sbyte*)propertyTargetHostId))!;
int targetPort = (int)ObsData.obs_data_get_int(settings, (sbyte*)propertyTargetPortId);
BeamReceiver.Connect(targetHost, targetPort);
BeamReceiver.Connect(NetworkInterfaceAddress, targetHost, targetPort);
}
}
}
Expand Down Expand Up @@ -202,7 +239,10 @@ public static unsafe void source_hide(void* data)
propertyConnectionTypePipeText = Module.ObsText("ConnectionTypePipeText"),
propertyConnectionTypeSocketId = "connection_type_socket"u8,
propertyConnectionTypeSocketCaption = Module.ObsText("ConnectionTypeSocketCaption"),
propertyConnectionTypeSocketText = Module.ObsText("ConnectionTypeSocketText")
propertyConnectionTypeSocketText = Module.ObsText("ConnectionTypeSocketText"),
propertyNetworkInterfaceListId = "network_interface_list"u8,
propertyNetworkInterfaceListCaption = Module.ObsText("NetworkInterfaceListCaption"),
propertyNetworkInterfaceListText = Module.ObsText("NetworkInterfaceListText")
)
{
// frame buffer
Expand All @@ -226,6 +266,27 @@ public static unsafe void source_hide(void* data)
ObsProperties.obs_property_set_long_description(connectionTypeSocketProperty, (sbyte*)propertyConnectionTypeSocketText);
ObsProperties.obs_property_set_modified_callback(connectionTypeSocketProperty, &ConnectionTypeSocketChangedEventHandler);

// network interface selection
var networkInterfacesList = ObsProperties.obs_properties_add_list(properties, (sbyte*)propertyNetworkInterfaceListId, (sbyte*)propertyNetworkInterfaceListCaption, obs_combo_type.OBS_COMBO_TYPE_LIST, obs_combo_format.OBS_COMBO_FORMAT_STRING);
ObsProperties.obs_property_set_long_description(networkInterfacesList, (sbyte*)propertyNetworkInterfaceListText);
fixed (byte* networkInterfaceAnyListItem = "Any: 0.0.0.0"u8)
ObsProperties.obs_property_list_add_string(networkInterfacesList, (sbyte*)networkInterfaceAnyListItem, (sbyte*)networkInterfaceAnyListItem);
foreach (var networkInterface in NetworkInterface.GetAllNetworkInterfaces())
{
if (networkInterface.OperationalStatus == OperationalStatus.Up)
{
foreach (var ip in networkInterface.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily != AddressFamily.InterNetwork)
continue;
string networkInterfaceDisplayName = networkInterface.Name + ": " + ip.Address + " / " + ip.IPv4Mask;
Module.Log($"Found network interface: {networkInterfaceDisplayName}", ObsLogLevel.Debug);
fixed (byte* networkInterfaceListItem = Encoding.UTF8.GetBytes(networkInterfaceDisplayName))
ObsProperties.obs_property_list_add_string(networkInterfacesList, (sbyte*)networkInterfaceListItem, (sbyte*)networkInterfaceListItem);
}
}
}

// target socket/pipe server address
ObsProperties.obs_property_set_long_description(ObsProperties.obs_properties_add_text(properties, (sbyte*)propertyTargetHostId, (sbyte*)propertyTargetHostCaption, obs_text_type.OBS_TEXT_DEFAULT), (sbyte*)propertyTargetHostText);

Expand Down Expand Up @@ -308,7 +369,10 @@ public static unsafe byte FrameBufferTimeChangedEventHandler(obs_properties* pro
[UnmanagedCallersOnly(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
public static unsafe byte ConnectionTypePipeChangedEventHandler(obs_properties* properties, obs_property* prop, obs_data* settings)
{
fixed (byte* propertyConnectionTypePipeId = "connection_type_pipe"u8, propertyConnectionTypeSocketId = "connection_type_socket"u8)
fixed (byte*
propertyConnectionTypePipeId = "connection_type_pipe"u8,
propertyConnectionTypeSocketId = "connection_type_socket"u8
)
{
var connectionTypePipe = Convert.ToBoolean(ObsData.obs_data_get_bool(settings, (sbyte*)propertyConnectionTypePipeId));
ObsData.obs_data_set_bool(settings, (sbyte*)propertyConnectionTypeSocketId, Convert.ToByte(!connectionTypePipe));
Expand All @@ -334,11 +398,13 @@ private static unsafe void ConnectionTypeChanged(bool connectionTypePipe, obs_pr
fixed (byte*
propertyTargetHostId = "host"u8,
propertyTargetPipeNameId = "pipe_name"u8,
propertyTargetPortId = "port"u8
propertyTargetPortId = "port"u8,
propertyNetworkInterfaceListId = "network_interface_list"u8
)
{
ObsProperties.obs_property_set_visible(ObsProperties.obs_properties_get(properties, (sbyte*)propertyTargetHostId), Convert.ToByte(!connectionTypePipe));
ObsProperties.obs_property_set_visible(ObsProperties.obs_properties_get(properties, (sbyte*)propertyTargetPipeNameId), Convert.ToByte(connectionTypePipe));
ObsProperties.obs_property_set_visible(ObsProperties.obs_properties_get(properties, (sbyte*)propertyNetworkInterfaceListId), Convert.ToByte(!connectionTypePipe));
ObsProperties.obs_property_set_visible(ObsProperties.obs_properties_get(properties, (sbyte*)propertyTargetPortId), Convert.ToByte(!connectionTypePipe));
Module.Log("Connection type changed to: " + (connectionTypePipe ? "pipe" : "socket"), ObsLogLevel.Debug);
}
Expand Down
2 changes: 1 addition & 1 deletion locale/en-US.ini
Expand Up @@ -3,7 +3,7 @@ EnableOutputText="Enable or disable the Beam output. Note that you cannot access
IdentifierCaption="Identifier"
IdentifierText="The identifier of the sender. This is used to identify the sender in the receiver. Defaults to \"BeamSender\" if empty."
NetworkInterfaceListCaption="Network interface"
NetworkInterfaceListText="List of currently available network interface, pick the one you want the sender to accept receiver connections on."
NetworkInterfaceListText="List of currently available network interface, pick the one you want to be used here."
AutomaticListenPortCaption="Choose listen port automatically"
AutomaticListenPortText="Select this to have a listen port chosen automatically (recommended)."
ListenPortCaption="Listen port"
Expand Down

0 comments on commit 09f45cf

Please sign in to comment.