static gboolean gst_multiudpsink_configure_client (GstMultiUDPSink * sink, GstUDPClient * client) { GInetSocketAddress *saddr = G_INET_SOCKET_ADDRESS (client->addr); GInetAddress *addr = g_inet_socket_address_get_address (saddr); GSocketFamily family = g_socket_address_get_family (G_SOCKET_ADDRESS (saddr)); GSocket *socket; GError *err = NULL; GST_DEBUG_OBJECT (sink, "configuring client %p", client); if (family == G_SOCKET_FAMILY_IPV6 && !sink->used_socket_v6) goto invalid_family; /* Select socket to send from for this address */ if (family == G_SOCKET_FAMILY_IPV6 || !sink->used_socket) socket = sink->used_socket_v6; else socket = sink->used_socket; if (g_inet_address_get_is_multicast (addr)) { GST_DEBUG_OBJECT (sink, "we have a multicast client %p", client); if (sink->auto_multicast) { GST_DEBUG_OBJECT (sink, "autojoining group"); if (!g_socket_join_multicast_group (socket, addr, FALSE, sink->multi_iface, &err)) goto join_group_failed; } GST_DEBUG_OBJECT (sink, "setting loop to %d", sink->loop); g_socket_set_multicast_loopback (socket, sink->loop); GST_DEBUG_OBJECT (sink, "setting ttl to %d", sink->ttl_mc); g_socket_set_multicast_ttl (socket, sink->ttl_mc); } else { GST_DEBUG_OBJECT (sink, "setting unicast ttl to %d", sink->ttl); g_socket_set_ttl (socket, sink->ttl); } return TRUE; /* ERRORS */ join_group_failed: { gst_multiudpsink_stop (GST_BASE_SINK (sink)); GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), ("Could not join multicast group: %s", err ? err->message : "unknown reason")); g_clear_error (&err); return FALSE; } invalid_family: { gst_multiudpsink_stop (GST_BASE_SINK (sink)); GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), ("Invalid address family (got %d)", family)); return FALSE; } }
static gboolean gst_net_client_clock_start (GstNetClientClock * self) { GSocketAddress *servaddr; GSocketAddress *myaddr; GInetAddress *inetaddr; GSocket *socket; GError *error = NULL; g_return_val_if_fail (self->priv->address != NULL, FALSE); g_return_val_if_fail (self->priv->servaddr == NULL, FALSE); socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &error); if (socket == NULL) goto no_socket; /* check address we're bound to, mostly for debugging purposes */ myaddr = g_socket_get_local_address (socket, &error); if (myaddr == NULL) goto getsockname_error; GST_DEBUG_OBJECT (self, "socket opened on UDP port %hd", g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (myaddr))); g_object_unref (myaddr); /* create target address */ inetaddr = g_inet_address_new_from_string (self->priv->address); if (inetaddr == NULL) goto bad_address; servaddr = g_inet_socket_address_new (inetaddr, self->priv->port); g_object_unref (inetaddr); g_assert (servaddr != NULL); GST_DEBUG_OBJECT (self, "will communicate with %s:%d", self->priv->address, self->priv->port); self->priv->cancel = g_cancellable_new (); self->priv->socket = socket; self->priv->servaddr = G_SOCKET_ADDRESS (servaddr); self->priv->thread = g_thread_try_new ("GstNetClientClock", gst_net_client_clock_thread, self, &error); if (error != NULL) goto no_thread; return TRUE; /* ERRORS */ no_socket: { GST_ERROR_OBJECT (self, "socket_new() failed: %s", error->message); g_error_free (error); return FALSE; } getsockname_error: { GST_ERROR_OBJECT (self, "get_local_address() failed: %s", error->message); g_error_free (error); g_object_unref (socket); return FALSE; } bad_address: { GST_ERROR_OBJECT (self, "inet_address_new_from_string('%s') failed", self->priv->address); g_object_unref (socket); return FALSE; } no_thread: { GST_ERROR_OBJECT (self, "could not create thread: %s", error->message); g_object_unref (self->priv->servaddr); self->priv->servaddr = NULL; g_object_unref (self->priv->socket); self->priv->socket = NULL; g_error_free (error); return FALSE; } }
GObject* spice_foreign_menu_listener_new (const gchar *address, GError **error) { GObject *listener = NULL; gchar *addr = NULL; g_return_val_if_fail (error == NULL || *error == NULL, NULL); addr = g_strdup (address); #ifdef G_OS_WIN32 if (addr == NULL) addr = g_strdup (g_getenv ("SPICE_FOREIGN_MENU_NAMEDPIPE")); if (addr == NULL) addr = g_strdup_printf ("\\\\.\\pipe\\SpiceForeignMenu-%" G_GUINT64_FORMAT, (guint64)GetCurrentProcessId ()); #else if (addr == NULL) addr = g_strdup (g_getenv ("SPICE_FOREIGN_MENU_SOCKET")); if (addr == NULL) addr = g_strdup_printf ("/tmp/SpiceForeignMenu-%" G_GUINT64_FORMAT ".uds", (guint64)getpid ()); #endif if (addr == NULL) { g_set_error (error, SPICE_FOREIGN_MENU_LISTENER_ERROR, SPICE_FOREIGN_MENU_LISTENER_ERROR_VALUE, #ifdef G_OS_WIN32 "Missing namedpipe address" #else "Missing socket address" #endif ); goto end; } g_unlink (addr); #ifdef G_OS_WIN32 { SpiceNamedPipe *np; listener = G_OBJECT (spice_named_pipe_listener_new ()); np = spice_win32_user_pipe_new (addr, error); if (!np) { g_object_unref (listener); listener = NULL; goto end; } spice_named_pipe_listener_add_named_pipe (SPICE_NAMED_PIPE_LISTENER (listener), np); } #else { listener = G_OBJECT (g_socket_listener_new ()); if (!g_socket_listener_add_address (G_SOCKET_LISTENER (listener), G_SOCKET_ADDRESS (g_unix_socket_address_new (addr)), G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, NULL, NULL, error)) g_warning ("failed to add address"); } #endif end: g_free (addr); return listener; }
/* create a socket for sending to remote machine */ static gboolean gst_multiudpsink_start (GstBaseSink * bsink) { GstMultiUDPSink *sink; GList *clients; GstUDPClient *client; GError *err = NULL; sink = GST_MULTIUDPSINK (bsink); sink->external_socket = FALSE; if (sink->socket) { GST_DEBUG_OBJECT (sink, "using configured socket"); if (g_socket_get_family (sink->socket) == G_SOCKET_FAMILY_IPV6) { sink->used_socket_v6 = G_SOCKET (g_object_ref (sink->socket)); sink->external_socket = TRUE; } else { sink->used_socket = G_SOCKET (g_object_ref (sink->socket)); sink->external_socket = TRUE; } } if (sink->socket_v6) { GST_DEBUG_OBJECT (sink, "using configured IPv6 socket"); g_return_val_if_fail (g_socket_get_family (sink->socket) != G_SOCKET_FAMILY_IPV6, FALSE); if (sink->used_socket_v6 && sink->used_socket_v6 != sink->socket_v6) { GST_ERROR_OBJECT (sink, "Provided different IPv6 sockets in socket and socket-v6 properties"); return FALSE; } sink->used_socket_v6 = G_SOCKET (g_object_ref (sink->socket_v6)); sink->external_socket = TRUE; } if (!sink->used_socket && !sink->used_socket_v6) { GSocketAddress *bind_addr; GInetAddress *bind_iaddr; if (sink->bind_address) { GSocketFamily family; bind_iaddr = g_inet_address_new_from_string (sink->bind_address); if (!bind_iaddr) { GList *results; GResolver *resolver; resolver = g_resolver_get_default (); results = g_resolver_lookup_by_name (resolver, sink->bind_address, sink->cancellable, &err); if (!results) { g_object_unref (resolver); goto name_resolve; } bind_iaddr = G_INET_ADDRESS (g_object_ref (results->data)); g_resolver_free_addresses (results); g_object_unref (resolver); } bind_addr = g_inet_socket_address_new (bind_iaddr, sink->bind_port); g_object_unref (bind_iaddr); family = g_socket_address_get_family (G_SOCKET_ADDRESS (bind_addr)); if ((sink->used_socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) { g_object_unref (bind_addr); goto no_socket; } g_socket_bind (sink->used_socket, bind_addr, TRUE, &err); if (err != NULL) goto bind_error; } else { /* create sender sockets if none available */ if ((sink->used_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) goto no_socket; bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); bind_addr = g_inet_socket_address_new (bind_iaddr, sink->bind_port); g_socket_bind (sink->used_socket, bind_addr, TRUE, &err); g_object_unref (bind_addr); g_object_unref (bind_iaddr); if (err != NULL) goto bind_error; if ((sink->used_socket_v6 = g_socket_new (G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) { GST_INFO_OBJECT (sink, "Failed to create IPv6 socket: %s", err->message); g_clear_error (&err); } else { bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6); bind_addr = g_inet_socket_address_new (bind_iaddr, sink->bind_port); g_socket_bind (sink->used_socket_v6, bind_addr, TRUE, &err); g_object_unref (bind_addr); g_object_unref (bind_iaddr); if (err != NULL) goto bind_error; } } } #ifdef SO_SNDBUF { socklen_t len; gint sndsize, ret; len = sizeof (sndsize); if (sink->buffer_size != 0) { sndsize = sink->buffer_size; GST_DEBUG_OBJECT (sink, "setting udp buffer of %d bytes", sndsize); /* set buffer size, Note that on Linux this is typically limited to a * maximum of around 100K. Also a minimum of 128 bytes is required on * Linux. */ if (sink->used_socket) { ret = setsockopt (g_socket_get_fd (sink->used_socket), SOL_SOCKET, SO_SNDBUF, (void *) &sndsize, len); if (ret != 0) { GST_ELEMENT_WARNING (sink, RESOURCE, SETTINGS, (NULL), ("Could not create a buffer of requested %d bytes, %d: %s", sndsize, ret, g_strerror (errno))); } } if (sink->used_socket_v6) { ret = setsockopt (g_socket_get_fd (sink->used_socket_v6), SOL_SOCKET, SO_SNDBUF, (void *) &sndsize, len); if (ret != 0) { GST_ELEMENT_WARNING (sink, RESOURCE, SETTINGS, (NULL), ("Could not create a buffer of requested %d bytes, %d: %s", sndsize, ret, g_strerror (errno))); } } } /* read the value of the receive buffer. Note that on linux this returns 2x the * value we set because the kernel allocates extra memory for metadata. * The default on Linux is about 100K (which is about 50K without metadata) */ if (sink->used_socket) { ret = getsockopt (g_socket_get_fd (sink->used_socket), SOL_SOCKET, SO_SNDBUF, (void *) &sndsize, &len); if (ret == 0) GST_DEBUG_OBJECT (sink, "have UDP buffer of %d bytes", sndsize); else GST_DEBUG_OBJECT (sink, "could not get UDP buffer size"); } if (sink->used_socket_v6) { ret = getsockopt (g_socket_get_fd (sink->used_socket_v6), SOL_SOCKET, SO_SNDBUF, (void *) &sndsize, &len); if (ret == 0) GST_DEBUG_OBJECT (sink, "have UDPv6 buffer of %d bytes", sndsize); else GST_DEBUG_OBJECT (sink, "could not get UDPv6 buffer size"); } } #endif #ifdef SO_BINDTODEVICE if (sink->multi_iface) { if (sink->used_socket) { setsockopt (g_socket_get_fd (sink->used_socket), SOL_SOCKET, SO_BINDTODEVICE, sink->multi_iface, strlen (sink->multi_iface)); } if (sink->used_socket_v6) { setsockopt (g_socket_get_fd (sink->used_socket_v6), SOL_SOCKET, SO_BINDTODEVICE, sink->multi_iface, strlen (sink->multi_iface)); } } #endif if (sink->used_socket) g_socket_set_broadcast (sink->used_socket, TRUE); if (sink->used_socket_v6) g_socket_set_broadcast (sink->used_socket_v6, TRUE); sink->bytes_to_serve = 0; sink->bytes_served = 0; gst_multiudpsink_setup_qos_dscp (sink, sink->used_socket); gst_multiudpsink_setup_qos_dscp (sink, sink->used_socket_v6); /* look for multicast clients and join multicast groups appropriately set also ttl and multicast loopback delivery appropriately */ for (clients = sink->clients; clients; clients = g_list_next (clients)) { client = (GstUDPClient *) clients->data; if (!gst_multiudpsink_configure_client (sink, client)) return FALSE; } return TRUE; /* ERRORS */ no_socket: { GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (NULL), ("Could not create socket: %s", err->message)); g_clear_error (&err); return FALSE; } bind_error: { GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (NULL), ("Failed to bind socket: %s", err->message)); g_clear_error (&err); return FALSE; } name_resolve: { GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (NULL), ("Failed to resolve bind address %s: %s", sink->bind_address, err->message)); g_clear_error (&err); return FALSE; } }
static GstFlowReturn gst_multiudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) { GstMultiUDPSink *sink; GList *clients; GOutputVector *vec; GstMapInfo *map; guint n_mem, i; gsize size; GstMemory *mem; gint num, no_clients; GError *err = NULL; sink = GST_MULTIUDPSINK (bsink); n_mem = gst_buffer_n_memory (buffer); if (n_mem == 0) goto no_data; /* allocated on the stack, the max number of memory blocks is limited so this * should not cause stack overflows */ vec = sink->vec; map = sink->map; size = 0; for (i = 0; i < n_mem; i++) { mem = gst_buffer_get_memory (buffer, i); gst_memory_map (mem, &map[i], GST_MAP_READ); vec[i].buffer = map[i].data; vec[i].size = map[i].size; size += map[i].size; } sink->bytes_to_serve += size; /* grab lock while iterating and sending to clients, this should be * fast as UDP never blocks */ g_mutex_lock (&sink->client_lock); GST_LOG_OBJECT (bsink, "about to send %" G_GSIZE_FORMAT " bytes in %u blocks", size, n_mem); no_clients = 0; num = 0; for (clients = sink->clients; clients; clients = g_list_next (clients)) { GstUDPClient *client; GSocket *socket; GSocketFamily family; gint count; client = (GstUDPClient *) clients->data; no_clients++; GST_LOG_OBJECT (sink, "sending %" G_GSIZE_FORMAT " bytes to client %p", size, client); family = g_socket_address_get_family (G_SOCKET_ADDRESS (client->addr)); /* Select socket to send from for this address */ if (family == G_SOCKET_FAMILY_IPV6 || !sink->used_socket) socket = sink->used_socket_v6; else socket = sink->used_socket; count = sink->send_duplicates ? client->refcount : 1; while (count--) { gssize ret; ret = g_socket_send_message (socket, client->addr, vec, n_mem, NULL, 0, 0, sink->cancellable, &err); if (G_UNLIKELY (ret < 0)) { if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) goto flushing; /* we continue after posting a warning, next packets might be ok * again */ if (size > UDP_MAX_SIZE) { GST_ELEMENT_WARNING (sink, RESOURCE, WRITE, ("Attempting to send a UDP packet larger than maximum size " "(%" G_GSIZE_FORMAT " > %d)", size, UDP_MAX_SIZE), ("Reason: %s", err ? err->message : "unknown reason")); } else { GST_ELEMENT_WARNING (sink, RESOURCE, WRITE, ("Error sending UDP packet"), ("Reason: %s", err ? err->message : "unknown reason")); } g_clear_error (&err); } else { num++; client->bytes_sent += ret; client->packets_sent++; sink->bytes_served += ret; } } } g_mutex_unlock (&sink->client_lock); /* unmap all memory again */ for (i = 0; i < n_mem; i++) { gst_memory_unmap (map[i].memory, &map[i]); gst_memory_unref (map[i].memory); } GST_LOG_OBJECT (sink, "sent %" G_GSIZE_FORMAT " bytes to %d (of %d) clients", size, num, no_clients); return GST_FLOW_OK; no_data: { return GST_FLOW_OK; } flushing: { GST_DEBUG ("we are flushing"); g_mutex_unlock (&sink->client_lock); g_clear_error (&err); /* unmap all memory */ for (i = 0; i < n_mem; i++) { gst_memory_unmap (map[i].memory, &map[i]); gst_memory_unref (map[i].memory); } return GST_FLOW_FLUSHING; } }
void gst_multiudpsink_remove (GstMultiUDPSink * sink, const gchar * host, gint port) { GList *find; GstUDPClient udpclient; GstUDPClient *client; GTimeVal now; udpclient.host = (gchar *) host; udpclient.port = port; g_mutex_lock (&sink->client_lock); find = g_list_find_custom (sink->clients, &udpclient, (GCompareFunc) client_compare); if (!find) goto not_found; client = (GstUDPClient *) find->data; GST_DEBUG_OBJECT (sink, "found %d clients with host %s, port %d", client->refcount, host, port); client->refcount--; if (client->refcount == 0) { GInetSocketAddress *saddr = G_INET_SOCKET_ADDRESS (client->addr); GInetAddress *addr = g_inet_socket_address_get_address (saddr); GSocketFamily family = g_socket_address_get_family (G_SOCKET_ADDRESS (saddr)); GSocket *socket; /* Select socket to send from for this address */ if (family == G_SOCKET_FAMILY_IPV6 || !sink->used_socket) socket = sink->used_socket_v6; else socket = sink->used_socket; GST_DEBUG_OBJECT (sink, "remove client with host %s, port %d", host, port); g_get_current_time (&now); client->disconnect_time = GST_TIMEVAL_TO_TIME (now); if (socket && sink->auto_multicast && g_inet_address_get_is_multicast (addr)) { GError *err = NULL; if (!g_socket_leave_multicast_group (socket, addr, FALSE, sink->multi_iface, &err)) { GST_DEBUG_OBJECT (sink, "Failed to leave multicast group: %s", err->message); g_clear_error (&err); } } /* Unlock to emit signal before we delete the actual client */ g_mutex_unlock (&sink->client_lock); g_signal_emit (G_OBJECT (sink), gst_multiudpsink_signals[SIGNAL_CLIENT_REMOVED], 0, host, port); g_mutex_lock (&sink->client_lock); sink->clients = g_list_delete_link (sink->clients, find); free_client (client); } g_mutex_unlock (&sink->client_lock); return; /* ERRORS */ not_found: { g_mutex_unlock (&sink->client_lock); GST_WARNING_OBJECT (sink, "client at host %s, port %d not found", host, port); return; } }
static gboolean gst_net_client_internal_clock_start (GstNetClientInternalClock * self) { GSocketAddress *servaddr; GSocketAddress *myaddr; GSocketAddress *anyaddr; GInetAddress *inetaddr; GSocket *socket; GError *error = NULL; GSocketFamily family; GPollFD dummy_pollfd; GResolver *resolver = NULL; GError *err = NULL; g_return_val_if_fail (self->address != NULL, FALSE); g_return_val_if_fail (self->servaddr == NULL, FALSE); /* create target address */ inetaddr = g_inet_address_new_from_string (self->address); if (inetaddr == NULL) { GList *results; resolver = g_resolver_get_default (); results = g_resolver_lookup_by_name (resolver, self->address, NULL, &err); if (!results) goto failed_to_resolve; inetaddr = G_INET_ADDRESS (g_object_ref (results->data)); g_resolver_free_addresses (results); g_object_unref (resolver); } family = g_inet_address_get_family (inetaddr); servaddr = g_inet_socket_address_new (inetaddr, self->port); g_object_unref (inetaddr); g_assert (servaddr != NULL); GST_DEBUG_OBJECT (self, "will communicate with %s:%d", self->address, self->port); socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &error); if (socket == NULL) goto no_socket; GST_DEBUG_OBJECT (self, "binding socket"); inetaddr = g_inet_address_new_any (family); anyaddr = g_inet_socket_address_new (inetaddr, 0); g_socket_bind (socket, anyaddr, TRUE, &error); g_object_unref (anyaddr); g_object_unref (inetaddr); if (error != NULL) goto bind_error; /* check address we're bound to, mostly for debugging purposes */ myaddr = g_socket_get_local_address (socket, &error); if (myaddr == NULL) goto getsockname_error; GST_DEBUG_OBJECT (self, "socket opened on UDP port %d", g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (myaddr))); g_object_unref (myaddr); self->cancel = g_cancellable_new (); self->made_cancel_fd = g_cancellable_make_pollfd (self->cancel, &dummy_pollfd); self->socket = socket; self->servaddr = G_SOCKET_ADDRESS (servaddr); self->thread = g_thread_try_new ("GstNetClientInternalClock", gst_net_client_internal_clock_thread, self, &error); if (error != NULL) goto no_thread; return TRUE; /* ERRORS */ no_socket: { GST_ERROR_OBJECT (self, "socket_new() failed: %s", error->message); g_error_free (error); return FALSE; } bind_error: { GST_ERROR_OBJECT (self, "bind failed: %s", error->message); g_error_free (error); g_object_unref (socket); return FALSE; } getsockname_error: { GST_ERROR_OBJECT (self, "get_local_address() failed: %s", error->message); g_error_free (error); g_object_unref (socket); return FALSE; } failed_to_resolve: { GST_ERROR_OBJECT (self, "resolving '%s' failed: %s", self->address, err->message); g_clear_error (&err); g_object_unref (resolver); return FALSE; } no_thread: { GST_ERROR_OBJECT (self, "could not create thread: %s", error->message); g_object_unref (self->servaddr); self->servaddr = NULL; g_object_unref (self->socket); self->socket = NULL; g_error_free (error); return FALSE; } }