From 2c5730cd99b9dc31f375cb87f7a9384116c3a394 Mon Sep 17 00:00:00 2001 From: Yuto Hamaguchi Date: Wed, 12 Feb 2025 10:02:30 +0100 Subject: [PATCH] gv: discovery interface selection Fixes #505 --- src/arvcameratest.c | 9 ++++ src/arvgvinterface.c | 119 +++++++++++++++++++++++++++++++++++++++---- src/arvgvinterface.h | 4 +- src/arvsystem.h | 4 +- src/arvtool.c | 9 ++++ tests/fakegv.c | 25 +++++++++ tests/meson.build | 4 +- 7 files changed, 158 insertions(+), 16 deletions(-) diff --git a/src/arvcameratest.c b/src/arvcameratest.c index 2b4f66f2e..7779e8f16 100644 --- a/src/arvcameratest.c +++ b/src/arvcameratest.c @@ -45,6 +45,7 @@ static gboolean arv_option_show_version = FALSE; static gboolean arv_option_gv_allow_broadcast_discovery_ack = FALSE; static char *arv_option_gv_port_range = NULL; static gboolean arv_option_native_buffers = FALSE; +static char *arv_option_gv_discovery_interface = NULL; /* clang-format off */ static const GOptionEntry arv_option_entries[] = @@ -227,6 +228,11 @@ static const GOptionEntry arv_option_entries[] = &arv_option_gv_port_range, "GV port range", "-" }, + { + "gv-discovery-interface", '\0', 0, G_OPTION_ARG_STRING, + &arv_option_gv_discovery_interface, "Discovery using the interface", + "" + }, { "native-buffers", '\0', 0, G_OPTION_ARG_NONE, &arv_option_native_buffers, "Enable native buffers", @@ -506,6 +512,9 @@ main (int argc, char **argv) if (arv_option_gv_allow_broadcast_discovery_ack) arv_set_interface_flags ("GigEVision", ARV_GV_INTERFACE_FLAGS_ALLOW_BROADCAST_DISCOVERY_ACK); + if (arv_option_gv_discovery_interface) + arv_gv_interface_set_discovery_interface_name (arv_option_gv_discovery_interface); + arv_enable_interface ("Fake"); arv_debug_enable (arv_option_debug_domains); diff --git a/src/arvgvinterface.c b/src/arvgvinterface.c index d29f00a15..9f3a1bf32 100644 --- a/src/arvgvinterface.c +++ b/src/arvgvinterface.c @@ -77,7 +77,7 @@ arv_gv_discover_socket_free (ArvGvDiscoverSocket *discover_socket) } static ArvGvDiscoverSocketList * -arv_gv_discover_socket_list_new (void) +arv_gv_discover_socket_list_new (const char *discovery_interface) { ArvGvDiscoverSocketList *socket_list; GSList *iter; @@ -92,7 +92,7 @@ arv_gv_discover_socket_list_new (void) return socket_list; for (iface_iter = ifaces; iface_iter != NULL; iface_iter = iface_iter->next) { - ArvGvDiscoverSocket *discover_socket = g_new0 (ArvGvDiscoverSocket, 1); + ArvGvDiscoverSocket *discover_socket; GSocketAddress *socket_address; GSocketAddress *socket_broadcast; GInetAddress *inet_address; @@ -101,6 +101,12 @@ arv_gv_discover_socket_list_new (void) char *inet_broadcast_string; GError *error = NULL; gint buffer_size = ARV_GV_INTERFACE_DISCOVERY_SOCKET_BUFFER_SIZE; + + if (discovery_interface != NULL) + if (g_strcmp0 (discovery_interface, arv_network_interface_get_name (iface_iter->data)) != 0) + continue; + + discover_socket = g_new0 (ArvGvDiscoverSocket, 1); socket_address = g_socket_address_new_from_native (arv_network_interface_get_addr(iface_iter->data), sizeof (struct sockaddr)); socket_broadcast = g_socket_address_new_from_native (arv_network_interface_get_broadaddr(iface_iter->data), @@ -339,6 +345,9 @@ arv_gv_interface_device_infos_unref (ArvGvInterfaceDeviceInfos *infos) typedef struct { GHashTable *devices; + + GMutex mutex; + char *discovery_interface; } ArvGvInterfacePrivate; struct _ArvGvInterface { @@ -354,7 +363,7 @@ struct _ArvGvInterfaceClass { G_DEFINE_TYPE_WITH_CODE (ArvGvInterface, arv_gv_interface, ARV_TYPE_INTERFACE, G_ADD_PRIVATE (ArvGvInterface)) static ArvGvInterfaceDeviceInfos * -_discover (GHashTable *devices, const char *device_id, gboolean allow_broadcast_discovery_ack) +_discover (GHashTable *devices, const char *device_id, gboolean allow_broadcast_discovery_ack, const char *discovery_interface) { ArvGvDiscoverSocketList *socket_list; GSList *iter; @@ -367,7 +376,7 @@ _discover (GHashTable *devices, const char *device_id, gboolean allow_broadcast_ if (devices != NULL) g_hash_table_remove_all (devices); - socket_list = arv_gv_discover_socket_list_new (); + socket_list = arv_gv_discover_socket_list_new (discovery_interface); if (socket_list->n_sockets < 1) { arv_gv_discover_socket_list_free (socket_list); @@ -484,8 +493,12 @@ static void arv_gv_interface_discover (ArvGvInterface *gv_interface) { int flags = arv_interface_get_flags (ARV_INTERFACE(gv_interface)); + char *discovery_interface; - _discover (gv_interface->priv->devices, NULL, flags & ARV_GV_INTERFACE_FLAGS_ALLOW_BROADCAST_DISCOVERY_ACK); + discovery_interface = arv_gv_interface_dup_discovery_interface_name(); + _discover (gv_interface->priv->devices, NULL, flags & ARV_GV_INTERFACE_FLAGS_ALLOW_BROADCAST_DISCOVERY_ACK, + discovery_interface); + g_free (discovery_interface); } static GInetAddress * @@ -553,6 +566,7 @@ arv_gv_interface_camera_locate (ArvGvInterface *gv_interface, GInetAddress *devi GList *ifaces; GList *iface_iter; struct sockaddr_in device_sockaddr; + char *discovery_interface; device_socket_address = g_inet_socket_address_new(device_address, ARV_GVCP_PORT); @@ -563,7 +577,8 @@ arv_gv_interface_camera_locate (ArvGvInterface *gv_interface, GInetAddress *devi for (iface_iter = ifaces; iface_iter != NULL; iface_iter = iface_iter->next) { struct sockaddr_in *sa = (struct sockaddr_in*)arv_network_interface_get_addr(iface_iter->data); struct sockaddr_in *mask = (struct sockaddr_in*)arv_network_interface_get_netmask(iface_iter->data); - if ((sa->sin_addr.s_addr & mask->sin_addr.s_addr) == (device_sockaddr.sin_addr.s_addr & mask->sin_addr.s_addr)) { + if ((sa->sin_addr.s_addr & mask->sin_addr.s_addr) == + (device_sockaddr.sin_addr.s_addr & mask->sin_addr.s_addr)) { GSocketAddress *socket_address = g_socket_address_new_from_native (arv_network_interface_get_addr(iface_iter->data), sizeof(struct sockaddr)); GInetAddress *inet_address = g_object_ref(g_inet_socket_address_get_address @@ -580,7 +595,9 @@ arv_gv_interface_camera_locate (ArvGvInterface *gv_interface, GInetAddress *devi g_list_free_full (ifaces, (GDestroyNotify) arv_network_interface_free); } - socket_list = arv_gv_discover_socket_list_new(); + discovery_interface = arv_gv_interface_dup_discovery_interface_name(); + socket_list = arv_gv_discover_socket_list_new (discovery_interface); + g_free (discovery_interface); if (socket_list->n_sockets < 1) { arv_gv_discover_socket_list_free (socket_list); @@ -728,6 +745,7 @@ arv_gv_interface_open_device (ArvInterface *interface, const char *device_id, GE { ArvDevice *device; ArvGvInterfaceDeviceInfos *device_infos; + char *discovery_interface; GError *local_error = NULL; int flags; @@ -739,7 +757,11 @@ arv_gv_interface_open_device (ArvInterface *interface, const char *device_id, GE } flags = arv_interface_get_flags (interface); - device_infos = _discover (NULL, device_id, flags & ARV_GVCP_DISCOVERY_PACKET_FLAGS_ALLOW_BROADCAST_ACK); + discovery_interface = arv_gv_interface_dup_discovery_interface_name(); + device_infos = _discover (NULL, device_id, flags & ARV_GVCP_DISCOVERY_PACKET_FLAGS_ALLOW_BROADCAST_ACK, + discovery_interface); + g_free (discovery_interface); + if (device_infos != NULL) { GInetAddress *device_address; @@ -758,6 +780,75 @@ arv_gv_interface_open_device (ArvInterface *interface, const char *device_id, GE static ArvInterface *arv_gv_interface = NULL; static GMutex arv_gv_interface_mutex; +static ArvInterface * +_get_instance (void) +{ + if (arv_gv_interface == NULL) + arv_gv_interface = g_object_new (ARV_TYPE_GV_INTERFACE, NULL); + + return ARV_INTERFACE (arv_gv_interface); +} + +/* + * arv_gv_interface_set_discovery_interface_name: + * @discovery_interface: (nullable): name of the discovery network interface + * + * Set the name of discovery network interface. If discovery_interface is %NULL, a discovery will be performed on every + * interfaces, which is the default behaviour. + * + * A call to [func@Aravis.update_device_list] may be necessary after the discovery interface has changed, in order to + * forget the previously discovered devices. + */ + +void +arv_gv_interface_set_discovery_interface_name (const char *discovery_interface) +{ + ArvInterface *interface; + + g_mutex_lock (&arv_gv_interface_mutex); + + interface = _get_instance(); + if (interface != NULL) { + ArvGvInterfacePrivate *priv = ARV_GV_INTERFACE (interface)->priv; + + g_mutex_lock (&priv->mutex); + g_clear_pointer (&priv->discovery_interface, g_free); + priv->discovery_interface = g_strdup (discovery_interface); + g_mutex_unlock (&priv->mutex); + } + + g_mutex_unlock (&arv_gv_interface_mutex); +} + +/* + * arv_gv_interface_dup_discovery_interface_name: + * + * Returns: the name of the interface used for device discovery, %NULL if discovery is performed on all the available + * interfaces. + */ + +char * +arv_gv_interface_dup_discovery_interface_name (void) +{ + ArvInterface *interface; + char *discovery_interface = NULL; + + g_mutex_lock (&arv_gv_interface_mutex); + + interface = _get_instance(); + if (interface != NULL) { + ArvGvInterfacePrivate *priv = ARV_GV_INTERFACE (interface)->priv; + + g_mutex_lock (&priv->mutex); + discovery_interface = g_strdup (priv->discovery_interface); + g_mutex_unlock (&priv->mutex); + } + + g_mutex_unlock (&arv_gv_interface_mutex); + + return discovery_interface; +} + /** * arv_gv_interface_get_instance: * @@ -769,14 +860,15 @@ static GMutex arv_gv_interface_mutex; ArvInterface * arv_gv_interface_get_instance (void) { + ArvInterface *gv_interface; + g_mutex_lock (&arv_gv_interface_mutex); - if (arv_gv_interface == NULL) - arv_gv_interface = g_object_new (ARV_TYPE_GV_INTERFACE, NULL); + gv_interface = _get_instance(); g_mutex_unlock (&arv_gv_interface_mutex); - return ARV_INTERFACE (arv_gv_interface); + return gv_interface; } void @@ -799,6 +891,9 @@ arv_gv_interface_init (ArvGvInterface *gv_interface) gv_interface->priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) arv_gv_interface_device_infos_unref); + g_mutex_init(&gv_interface->priv->mutex); + gv_interface->priv->discovery_interface = NULL; + } static void @@ -808,6 +903,8 @@ arv_gv_interface_finalize (GObject *object) g_hash_table_unref (gv_interface->priv->devices); gv_interface->priv->devices = NULL; + g_clear_pointer (&gv_interface->priv->discovery_interface, g_free); + g_mutex_clear (&gv_interface->priv->mutex); G_OBJECT_CLASS (arv_gv_interface_parent_class)->finalize (object); } diff --git a/src/arvgvinterface.h b/src/arvgvinterface.h index d8bc42c23..d678ca887 100644 --- a/src/arvgvinterface.h +++ b/src/arvgvinterface.h @@ -47,7 +47,9 @@ typedef enum { #define ARV_TYPE_GV_INTERFACE (arv_gv_interface_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGvInterface, arv_gv_interface, ARV, GV_INTERFACE, ArvInterface) -ARV_API ArvInterface * arv_gv_interface_get_instance (void); +ARV_API ArvInterface * arv_gv_interface_get_instance (void); +ARV_API void arv_gv_interface_set_discovery_interface_name (const char *discovery_interface); +ARV_API char * arv_gv_interface_dup_discovery_interface_name (void); G_END_DECLS diff --git a/src/arvsystem.h b/src/arvsystem.h index 33ee3593f..a767bd481 100644 --- a/src/arvsystem.h +++ b/src/arvsystem.h @@ -33,8 +33,8 @@ G_BEGIN_DECLS ARV_API unsigned int arv_get_n_interfaces (void); -ARV_API ArvInterface* arv_get_interface (unsigned int index); -ARV_API ArvInterface* arv_get_interface_by_id (const char* interface_id); +ARV_API ArvInterface* arv_get_interface (unsigned int index); +ARV_API ArvInterface* arv_get_interface_by_id (const char* interface_id); ARV_API const char * arv_get_interface_id (unsigned int index); ARV_API const char * arv_get_interface_protocol (unsigned int index); ARV_API void arv_enable_interface (const char *interface_id); diff --git a/src/arvtool.c b/src/arvtool.c index 2122a9d33..971f375aa 100644 --- a/src/arvtool.c +++ b/src/arvtool.c @@ -37,6 +37,7 @@ static gboolean arv_option_gv_allow_broadcast_discovery_ack = FALSE; static gboolean arv_option_show_time = FALSE; static gboolean arv_option_show_version = FALSE; static char *arv_option_gv_port_range = NULL; +static char *arv_option_gv_discovery_interface = NULL; static const GOptionEntry arv_option_entries[] = { @@ -82,6 +83,11 @@ static const GOptionEntry arv_option_entries[] = &arv_option_gv_port_range, "GV port range", "-" }, + { + "gv-discovery-interface", '\0', 0, G_OPTION_ARG_STRING, + &arv_option_gv_discovery_interface, "Discovery using the interface", + "" + }, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_option_debug_domains, NULL, @@ -802,6 +808,9 @@ main (int argc, char **argv) if (arv_option_gv_allow_broadcast_discovery_ack) arv_set_interface_flags ("GigEVision", ARV_GV_INTERFACE_FLAGS_ALLOW_BROADCAST_DISCOVERY_ACK); + if (arv_option_gv_discovery_interface) + arv_gv_interface_set_discovery_interface_name (arv_option_gv_discovery_interface); + device_id = arv_option_device_address != NULL ? arv_option_device_address : (is_glob_pattern ? NULL : arv_option_device_selection); diff --git a/tests/fakegv.c b/tests/fakegv.c index e1ae08b9f..5ca65cca0 100644 --- a/tests/fakegv.c +++ b/tests/fakegv.c @@ -6,9 +6,34 @@ static ArvCamera *camera = NULL; static void discovery_test (void) { + ArvCamera *test_camera; + char *discovery_iface; int n_devices; int i; + arv_gv_interface_set_discovery_interface_name("not-an-iface"); + + arv_update_device_list(); + + discovery_iface = arv_gv_interface_dup_discovery_interface_name(); + g_assert (discovery_iface != NULL); + g_assert_cmpstr("not-an-iface", ==, discovery_iface); + g_free (discovery_iface); + + test_camera = arv_camera_new ("Aravis-GVTest", NULL); + g_assert (test_camera == NULL); + + arv_gv_interface_set_discovery_interface_name(NULL); + + arv_update_device_list(); + + discovery_iface = arv_gv_interface_dup_discovery_interface_name(); + g_assert (discovery_iface == NULL); + + test_camera = arv_camera_new ("Aravis-GVTest", NULL); + g_assert (ARV_IS_CAMERA(test_camera)); + g_clear_object (&test_camera); + n_devices = arv_get_n_devices (); for (i = 0; i < n_devices; i++) { diff --git a/tests/meson.build b/tests/meson.build index 89892d608..76835464b 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -13,7 +13,7 @@ if get_option('tests') ], env:['DEBUGINFOD_URLS=', 'ARV_TEST_IGNORE_BUFFER='], exclude_suites:['python'], - timeout_multiplier:10 + timeout_multiplier:5 ) tests = [ @@ -36,7 +36,7 @@ if get_option('tests') link_with: aravis_library, dependencies: aravis_dependencies, include_directories: [library_inc]) - test (t[0], exe, suite: t[1]) + test (t[0], exe, suite: t[1], timeout: 60) endforeach py_script_config_data = configuration_data ()