diff --git a/src/arvgvdevice.c b/src/arvgvdevice.c index da3d38353..bdf016c69 100644 --- a/src/arvgvdevice.c +++ b/src/arvgvdevice.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -667,6 +668,7 @@ auto_packet_size (ArvGvDevice *gv_device, gboolean exit_early, GError **error) char *buffer; guint last_size = 0; gboolean success; + char *bindtodevice_name = NULL; g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), 1500); @@ -697,6 +699,21 @@ auto_packet_size (ArvGvDevice *gv_device, gboolean exit_early, GError **error) interface_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (priv->io_data->interface_address)); socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, NULL); +#ifdef SO_BINDTODEVICE + bindtodevice_name = arv_gv_interface_dup_discovery_bindtodevice_name(); + if (bindtodevice_name != NULL) { + if (setsockopt (g_socket_get_fd (socket), SOL_SOCKET, SO_BINDTODEVICE, + bindtodevice_name, strlen (bindtodevice_name)) != 0) { + arv_warning_interface ("[GvDevice::new] " + "Failed to bind socket to device %s: %s", + bindtodevice_name, g_strerror (errno)); + } else { + arv_info_interface ("[GvDevice::new] " + "Socket bound to device %s", bindtodevice_name); + } + g_free(bindtodevice_name); + } +#endif interface_socket_address = arv_socket_bind_with_range (socket, interface_address, 0, FALSE, NULL); local_address = G_INET_SOCKET_ADDRESS (g_socket_get_local_address (socket, NULL)); port = g_inet_socket_address_get_port (local_address); @@ -1989,6 +2006,7 @@ arv_gv_device_constructed (GObject *object) char *address_string; guint32 capabilities; guint32 device_mode; + char *bindtodevice_name; G_OBJECT_CLASS (arv_gv_device_parent_class)->constructed (object); @@ -2016,6 +2034,21 @@ arv_gv_device_constructed (GObject *object) io_data->socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, NULL); +#ifdef SO_BINDTODEVICE + bindtodevice_name = arv_gv_interface_dup_discovery_bindtodevice_name(); + if (bindtodevice_name != NULL) { + if (setsockopt (g_socket_get_fd (io_data->socket), SOL_SOCKET, SO_BINDTODEVICE, + bindtodevice_name, strlen (bindtodevice_name)) != 0) { + arv_warning_interface ("[GvDevice::new] " + "Failed to bind socket to device %s: %s", + bindtodevice_name, g_strerror (errno)); + } else { + arv_info_interface ("[GvDevice::new] " + "Socket bound to device %s", bindtodevice_name); + } + g_free(bindtodevice_name); + } +#endif io_data->interface_address = arv_socket_bind_with_range (io_data->socket, priv->interface_address, 0, FALSE, &local_error); diff --git a/src/arvgvinterface.c b/src/arvgvinterface.c index a2cf84500..248c6aa43 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 (const char *discovery_interface) +arv_gv_discover_socket_list_new (const char *discovery_interface, const char* discovery_bindtodevice) { ArvGvDiscoverSocketList *socket_list; GSList *iter; @@ -123,6 +123,22 @@ arv_gv_discover_socket_list_new (const char *discovery_interface) discover_socket->socket = g_socket_new (g_inet_address_get_family (inet_address), G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, NULL); + +#ifdef SO_BINDTODEVICE + if (discovery_bindtodevice != NULL) { + if (setsockopt (g_socket_get_fd (discover_socket->socket), SOL_SOCKET, SO_BINDTODEVICE, + discovery_bindtodevice, strlen (discovery_bindtodevice)) != 0) { + arv_warning_interface ("[ArvGVInterface::discover_socket_list_new] " + "Failed to bind socket to device %s: %s", + discovery_bindtodevice, g_strerror (errno)); + } else { + arv_info_interface ("[ArvGVInterface::discover_socket_list_new] " + "Socket bound to device %s", discovery_bindtodevice); + } + } +#endif + + arv_socket_set_recv_buffer_size (g_socket_get_fd (discover_socket->socket), buffer_size); discover_socket->interface_address = arv_socket_bind_with_range (discover_socket->socket, inet_address, @@ -348,6 +364,7 @@ typedef struct { GMutex mutex; char *discovery_interface; + char *discovery_bindtodevice; } ArvGvInterfacePrivate; struct _ArvGvInterface { @@ -363,7 +380,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, const char *discovery_interface) +_discover (GHashTable *devices, const char *device_id, gboolean allow_broadcast_discovery_ack, const char *discovery_interface, const char* discovery_bindtodevice) { ArvGvDiscoverSocketList *socket_list; GSList *iter; @@ -376,7 +393,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 (discovery_interface); + socket_list = arv_gv_discover_socket_list_new (discovery_interface, discovery_bindtodevice); if (socket_list->n_sockets < 1) { arv_gv_discover_socket_list_free (socket_list); @@ -494,10 +511,12 @@ arv_gv_interface_discover (ArvGvInterface *gv_interface) { int flags = arv_interface_get_flags (ARV_INTERFACE(gv_interface)); char *discovery_interface; + char *discovery_bindtodevice; discovery_interface = arv_gv_interface_dup_discovery_interface_name(); + discovery_bindtodevice = arv_gv_interface_dup_discovery_bindtodevice_name(); _discover (gv_interface->priv->devices, NULL, flags & ARV_GV_INTERFACE_FLAGS_ALLOW_BROADCAST_DISCOVERY_ACK, - discovery_interface); + discovery_interface, discovery_bindtodevice); g_free (discovery_interface); } @@ -567,6 +586,7 @@ arv_gv_interface_camera_locate (ArvGvInterface *gv_interface, GInetAddress *devi GList *iface_iter; struct sockaddr_in device_sockaddr; char *discovery_interface; + char *discovery_bindtodevice; device_socket_address = g_inet_socket_address_new(device_address, ARV_GVCP_PORT); @@ -596,7 +616,8 @@ arv_gv_interface_camera_locate (ArvGvInterface *gv_interface, GInetAddress *devi } discovery_interface = arv_gv_interface_dup_discovery_interface_name(); - socket_list = arv_gv_discover_socket_list_new (discovery_interface); + discovery_bindtodevice = arv_gv_interface_dup_discovery_bindtodevice_name(); + socket_list = arv_gv_discover_socket_list_new (discovery_interface, discovery_bindtodevice); g_free (discovery_interface); if (socket_list->n_sockets < 1) { @@ -686,6 +707,7 @@ _open_device (ArvInterface *interface, GHashTable *devices, const char *device_i device_infos = g_hash_table_lookup (devices, device_id); if (device_infos == NULL) { + arv_info_interface ("[GvDevice::_open device] Device info not found, seeing if ID (%s) is an address", device_id); struct addrinfo hints; struct addrinfo *servinfo, *endpoint; @@ -746,6 +768,7 @@ arv_gv_interface_open_device (ArvInterface *interface, const char *device_id, GE ArvDevice *device; ArvGvInterfaceDeviceInfos *device_infos; char *discovery_interface; + char *discovery_bindtodevice; GError *local_error = NULL; int flags; @@ -758,8 +781,9 @@ arv_gv_interface_open_device (ArvInterface *interface, const char *device_id, GE flags = arv_interface_get_flags (interface); discovery_interface = arv_gv_interface_dup_discovery_interface_name(); + discovery_bindtodevice = arv_gv_interface_dup_discovery_bindtodevice_name(); device_infos = _discover (NULL, device_id, flags & ARV_GVCP_DISCOVERY_PACKET_FLAGS_ALLOW_BROADCAST_ACK, - discovery_interface); + discovery_interface, discovery_bindtodevice); g_free (discovery_interface); if (device_infos != NULL) { @@ -853,6 +877,70 @@ arv_gv_interface_dup_discovery_interface_name (void) return discovery_interface; } +/* + * arv_gv_interface_set_discovery_bindtodevice_name: + * @bindtodevice_name: (nullable): name of the device to bind the discovery to + * + * Set the name of the device to bind the discovery to. If bindtodevice_name 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. + * + * Since: 0.8.34 + */ + +void +arv_gv_interface_set_discovery_bindtodevice_name (const char *bindtodevice_name) +{ + 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_bindtodevice, g_free); + priv->discovery_bindtodevice = g_strdup (bindtodevice_name); + g_mutex_unlock (&priv->mutex); + } + + g_mutex_unlock (&arv_gv_interface_mutex); +} + +/* + * arv_gv_interface_dup_discovery_bindtodevice_name: + * + * Returns: the name of the device used for device discovery, %NULL if discovery is performed on all the available + * interfaces. + * + * Since: 0.8.34 + */ + +char * +arv_gv_interface_dup_discovery_bindtodevice_name (void) +{ + ArvInterface *interface; + char *discovery_bindtodevice = 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_bindtodevice = g_strdup (priv->discovery_bindtodevice); + g_mutex_unlock (&priv->mutex); + } + + g_mutex_unlock (&arv_gv_interface_mutex); + + return discovery_bindtodevice; +} + /** * arv_gv_interface_get_instance: * @@ -897,7 +985,7 @@ arv_gv_interface_init (ArvGvInterface *gv_interface) (GDestroyNotify) arv_gv_interface_device_infos_unref); g_mutex_init(&gv_interface->priv->mutex); gv_interface->priv->discovery_interface = NULL; - + gv_interface->priv->discovery_bindtodevice = NULL; } static void @@ -908,6 +996,7 @@ 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_clear_pointer (&gv_interface->priv->discovery_bindtodevice, 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 c3ad6aa24..ccc49ab08 100644 --- a/src/arvgvinterface.h +++ b/src/arvgvinterface.h @@ -50,6 +50,8 @@ ARV_API G_DECLARE_FINAL_TYPE (ArvGvInterface, arv_gv_interface, ARV, GV_INTERFAC 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); +ARV_API void arv_gv_interface_set_discovery_bindtodevice_name(const char *bindtodevice_name); +ARV_API char * arv_gv_interface_dup_discovery_bindtodevice_name(void); G_END_DECLS diff --git a/src/arvgvstream.c b/src/arvgvstream.c index e1426c525..9e53e152d 100644 --- a/src/arvgvstream.c +++ b/src/arvgvstream.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -1599,6 +1600,7 @@ arv_gv_stream_constructed (GObject *object) const guint8 *address_bytes; GInetSocketAddress *local_address; guint packet_size; + char *bindtodevice_name; G_OBJECT_CLASS (arv_gv_stream_parent_class)->constructed (object); @@ -1659,6 +1661,21 @@ arv_gv_stream_constructed (GObject *object) (G_INET_SOCKET_ADDRESS (arv_gv_device_get_device_address (priv->gv_device))); priv->thread_data->socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, NULL); +#ifdef SO_BINDTODEVICE + bindtodevice_name = arv_gv_interface_dup_discovery_bindtodevice_name(); + if (bindtodevice_name != NULL) { + if (setsockopt (g_socket_get_fd (priv->thread_data->socket), SOL_SOCKET, SO_BINDTODEVICE, + bindtodevice_name, strlen (bindtodevice_name)) != 0) { + arv_warning_interface ("[GvDevice::new] " + "Failed to bind socket to device %s: %s", + bindtodevice_name, g_strerror (errno)); + } else { + arv_info_interface ("[GvDevice::new] " + "Socket bound to device %s", bindtodevice_name); + } + g_free(bindtodevice_name); + } +#endif priv->thread_data->device_address = g_object_ref (device_address); priv->thread_data->interface_address = g_object_ref (interface_address); priv->thread_data->device_socket_address = g_inet_socket_address_new (device_address, ARV_GVCP_PORT);