static void gst_udpsink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstUDPSink *udpsink; udpsink = GST_UDPSINK (object); /* remove old host */ gst_multiudpsink_remove (GST_MULTIUDPSINK (udpsink), udpsink->host, udpsink->port); switch (prop_id) { case PROP_HOST: g_free (udpsink->host); udpsink->host = g_value_dup_string (value); break; case PROP_PORT: udpsink->port = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } /* add new host */ gst_multiudpsink_add (GST_MULTIUDPSINK (udpsink), udpsink->host, udpsink->port); }
static void gst_multiudpsink_finalize (GObject * object) { GstMultiUDPSink *sink; sink = GST_MULTIUDPSINK (object); g_list_foreach (sink->clients, (GFunc) free_client, NULL); g_list_free (sink->clients); if (sink->socket) g_object_unref (sink->socket); sink->socket = NULL; if (sink->used_socket) g_object_unref (sink->used_socket); sink->used_socket = NULL; if (sink->cancellable) g_object_unref (sink->cancellable); sink->cancellable = NULL; g_mutex_clear (&sink->client_lock); G_OBJECT_CLASS (parent_class)->finalize (object); }
static GstStateChangeReturn gst_multiudpsink_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret; GstMultiUDPSink *sink; sink = GST_MULTIUDPSINK (element); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: if (!gst_multiudpsink_init_send (sink)) goto no_init; break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: gst_multiudpsink_close (sink); break; default: break; } return ret; /* ERRORS */ no_init: { /* _init_send() posted specific error already */ return GST_STATE_CHANGE_FAILURE; } }
static void gst_udpsink_init (GstUDPSink * udpsink) { udpsink->host = g_strdup (UDP_DEFAULT_HOST); udpsink->port = UDP_DEFAULT_PORT; gst_multiudpsink_add (GST_MULTIUDPSINK (udpsink), udpsink->host, udpsink->port); }
static void gst_multiudpsink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstMultiUDPSink *udpsink; udpsink = GST_MULTIUDPSINK (object); switch (prop_id) { case PROP_SOCKET: if (udpsink->socket != NULL && udpsink->socket != udpsink->used_socket && udpsink->close_socket) { GError *err = NULL; if (!g_socket_close (udpsink->socket, &err)) { GST_ERROR ("failed to close socket %p: %s", udpsink->socket, err->message); g_clear_error (&err); } } if (udpsink->socket) g_object_unref (udpsink->socket); udpsink->socket = g_value_dup_object (value); GST_DEBUG_OBJECT (udpsink, "setting socket to %p", udpsink->socket); break; case PROP_CLOSE_SOCKET: udpsink->close_socket = g_value_get_boolean (value); break; case PROP_CLIENTS: gst_multiudpsink_set_clients_string (udpsink, g_value_get_string (value)); break; case PROP_AUTO_MULTICAST: udpsink->auto_multicast = g_value_get_boolean (value); break; case PROP_TTL: udpsink->ttl = g_value_get_int (value); break; case PROP_TTL_MC: udpsink->ttl_mc = g_value_get_int (value); break; case PROP_LOOP: udpsink->loop = g_value_get_boolean (value); break; case PROP_QOS_DSCP: udpsink->qos_dscp = g_value_get_int (value); gst_multiudpsink_setup_qos_dscp (udpsink); break; case PROP_SEND_DUPLICATES: udpsink->send_duplicates = g_value_get_boolean (value); break; case PROP_BUFFER_SIZE: udpsink->buffer_size = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_udpsink_set_uri (GstUDPSink * sink, const gchar * uri) { gchar *protocol; gchar *location; gchar *colptr; protocol = gst_uri_get_protocol (uri); if (strcmp (protocol, "udp") != 0) goto wrong_protocol; g_free (protocol); location = gst_uri_get_location (uri); if (!location) return FALSE; colptr = strstr (location, ":"); gst_multiudpsink_remove (GST_MULTIUDPSINK (sink), sink->host, sink->port); if (colptr != NULL) { g_free (sink->host); sink->host = g_strndup (location, colptr - location); sink->port = atoi (colptr + 1); } else { g_free (sink->host); sink->host = g_strdup (location); sink->port = UDP_DEFAULT_PORT; } g_free (location); gst_multiudpsink_add (GST_MULTIUDPSINK (sink), sink->host, sink->port); gst_udpsink_update_uri (sink); return TRUE; /* ERRORS */ wrong_protocol: { g_free (protocol); GST_ELEMENT_ERROR (sink, RESOURCE, READ, (NULL), ("error parsing uri %s: wrong protocol (%s != udp)", uri, protocol)); return FALSE; } }
static void gst_udpsink_init (GstUDPSink * udpsink, GstUDPSinkClass * klass) { udpsink->host = g_strdup (UDP_DEFAULT_HOST); udpsink->port = UDP_DEFAULT_PORT; udpsink->uri = g_strdup_printf ("udp://%s:%d", udpsink->host, udpsink->port); gst_multiudpsink_add (GST_MULTIUDPSINK (udpsink), udpsink->host, udpsink->port); }
static void gst_multiudpsink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstMultiUDPSink *udpsink; udpsink = GST_MULTIUDPSINK (object); switch (prop_id) { case PROP_BYTES_TO_SERVE: g_value_set_uint64 (value, udpsink->bytes_to_serve); break; case PROP_BYTES_SERVED: g_value_set_uint64 (value, udpsink->bytes_served); break; case PROP_SOCKET: g_value_set_object (value, udpsink->socket); break; case PROP_CLOSE_SOCKET: g_value_set_boolean (value, udpsink->close_socket); break; case PROP_USED_SOCKET: g_value_set_object (value, udpsink->used_socket); break; case PROP_CLIENTS: g_value_take_string (value, gst_multiudpsink_get_clients_string (udpsink)); break; case PROP_AUTO_MULTICAST: g_value_set_boolean (value, udpsink->auto_multicast); break; case PROP_TTL: g_value_set_int (value, udpsink->ttl); break; case PROP_TTL_MC: g_value_set_int (value, udpsink->ttl_mc); break; case PROP_LOOP: g_value_set_boolean (value, udpsink->loop); break; case PROP_QOS_DSCP: g_value_set_int (value, udpsink->qos_dscp); break; case PROP_SEND_DUPLICATES: g_value_set_boolean (value, udpsink->send_duplicates); break; case PROP_BUFFER_SIZE: g_value_set_int (value, udpsink->buffer_size); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_multiudpsink_unlock_stop (GstBaseSink * bsink) { GstMultiUDPSink *sink; sink = GST_MULTIUDPSINK (bsink); g_cancellable_reset (sink->cancellable); return TRUE; }
static gboolean gst_udpsink_set_uri (GstUDPSink * sink, const gchar * uri) { gst_multiudpsink_remove (GST_MULTIUDPSINK (sink), sink->host, sink->port); if (!gst_udp_parse_uri (uri, &sink->host, &sink->port)) goto wrong_uri; g_free (sink->uri); sink->uri = g_strdup (uri); gst_multiudpsink_add (GST_MULTIUDPSINK (sink), sink->host, sink->port); return TRUE; /* ERRORS */ wrong_uri: { GST_ELEMENT_ERROR (sink, RESOURCE, READ, (NULL), ("error parsing uri %s", uri)); return FALSE; } }
static void gst_multiudpsink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstMultiUDPSink *udpsink; udpsink = GST_MULTIUDPSINK (object); switch (prop_id) { case PROP_SOCKFD: if (udpsink->sockfd >= 0 && udpsink->sockfd != udpsink->sock && udpsink->closefd) CLOSE_SOCKET (udpsink->sockfd); udpsink->sockfd = g_value_get_int (value); GST_DEBUG_OBJECT (udpsink, "setting SOCKFD to %d", udpsink->sockfd); break; case PROP_CLOSEFD: udpsink->closefd = g_value_get_boolean (value); break; case PROP_CLIENTS: gst_multiudpsink_set_clients_string (udpsink, g_value_get_string (value)); break; case PROP_AUTO_MULTICAST: udpsink->auto_multicast = g_value_get_boolean (value); break; case PROP_TTL: udpsink->ttl = g_value_get_int (value); break; case PROP_TTL_MC: udpsink->ttl_mc = g_value_get_int (value); break; case PROP_LOOP: udpsink->loop = g_value_get_boolean (value); break; case PROP_QOS_DSCP: udpsink->qos_dscp = g_value_get_int (value); gst_multiudpsink_setup_qos_dscp (udpsink); break; case PROP_SEND_DUPLICATES: udpsink->send_duplicates = g_value_get_boolean (value); break; case PROP_BUFFER_SIZE: udpsink->buffer_size = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void gst_multiudpsink_finalize (GObject * object) { GstMultiUDPSink *sink; sink = GST_MULTIUDPSINK (object); g_list_foreach (sink->clients, (GFunc) free_client, NULL); g_list_free (sink->clients); if (sink->sockfd >= 0 && sink->closefd) CLOSE_SOCKET (sink->sockfd); g_mutex_free (sink->client_lock); WSA_CLEANUP (object); G_OBJECT_CLASS (parent_class)->finalize (object); }
static gboolean gst_multiudpsink_stop (GstBaseSink * bsink) { GstMultiUDPSink *udpsink; udpsink = GST_MULTIUDPSINK (bsink); if (udpsink->used_socket) { if (udpsink->close_socket || !udpsink->external_socket) { GError *err = NULL; if (!g_socket_close (udpsink->used_socket, &err)) { GST_ERROR_OBJECT (udpsink, "Failed to close socket: %s", err->message); g_clear_error (&err); } } g_object_unref (udpsink->used_socket); udpsink->used_socket = NULL; } return TRUE; }
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; vec = g_new (GOutputVector, n_mem); map = g_new (GstMapInfo, n_mem); 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); if (map[i].size > UDP_MAX_SIZE) { GST_WARNING ("Attempting to send a UDP packet larger than maximum " "size (%" G_GSIZE_FORMAT " > %d)", map[i].size, UDP_MAX_SIZE); } 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", size); no_clients = 0; num = 0; for (clients = sink->clients; clients; clients = g_list_next (clients)) { GstUDPClient *client; gint count; client = (GstUDPClient *) clients->data; no_clients++; GST_LOG_OBJECT (sink, "sending %" G_GSIZE_FORMAT " bytes to client %p", size, client); count = sink->send_duplicates ? client->refcount : 1; while (count--) { gssize ret; ret = g_socket_send_message (sink->used_socket, client->addr, vec, n_mem, NULL, 0, 0, sink->cancellable, &err); if (ret < 0) goto send_error; 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); } g_free (vec); g_free (map); 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; } send_error: { g_mutex_unlock (&sink->client_lock); GST_DEBUG ("got send error %s", err->message); g_clear_error (&err); return GST_FLOW_ERROR; } }
static GstFlowReturn gst_multiudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) { GstMultiUDPSink *sink; gint ret, size, num = 0, no_clients = 0; guint8 *data; GList *clients; gint len; sink = GST_MULTIUDPSINK (bsink); size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); if (size > UDP_MAX_SIZE) { GST_WARNING ("Attempting to send a UDP packet larger than maximum " "size (%d > %d)", size, UDP_MAX_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 %d bytes", size); for (clients = sink->clients; clients; clients = g_list_next (clients)) { GstUDPClient *client; gint count; client = (GstUDPClient *) clients->data; no_clients++; GST_LOG_OBJECT (sink, "sending %d bytes to client %p", size, client); count = sink->send_duplicates ? client->refcount : 1; while (count--) { while (TRUE) { len = gst_udp_get_sockaddr_length (&client->theiraddr); ret = sendto (*client->sock, #ifdef G_OS_WIN32 (char *) data, #else data, #endif size, 0, (struct sockaddr *) &client->theiraddr, len); if (ret < 0) { /* some error, just warn, it's likely recoverable and we don't want to * break streaming. We break so that we stop retrying for this client. */ if (!socket_error_is_ignorable ()) { gchar *errormessage = socket_last_error_message (); GST_WARNING_OBJECT (sink, "client %p gave error %d (%s)", client, socket_last_error_code (), errormessage); g_free (errormessage); break; } } else { num++; client->bytes_sent += ret; client->packets_sent++; sink->bytes_served += ret; break; } } } } g_mutex_unlock (sink->client_lock); GST_LOG_OBJECT (sink, "sent %d bytes to %d (of %d) clients", size, num, no_clients); return GST_FLOW_OK; }
/* 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; } }
static GstFlowReturn gst_multiudpsink_render_list (GstBaseSink * bsink, GstBufferList * list) { GstMultiUDPSink *sink; GList *clients; gint ret, size = 0, num = 0, no_clients = 0; struct iovec *iov; struct msghdr msg = { 0 }; GstBufferListIterator *it; guint gsize; GstBuffer *buf; sink = GST_MULTIUDPSINK (bsink); g_return_val_if_fail (list != NULL, GST_FLOW_ERROR); it = gst_buffer_list_iterate (list); g_return_val_if_fail (it != NULL, GST_FLOW_ERROR); while (gst_buffer_list_iterator_next_group (it)) { msg.msg_iovlen = 0; size = 0; if ((gsize = gst_buffer_list_iterator_n_buffers (it)) == 0) { goto invalid_list; } iov = (struct iovec *) g_malloc (gsize * sizeof (struct iovec)); msg.msg_iov = iov; while ((buf = gst_buffer_list_iterator_next (it))) { if (GST_BUFFER_SIZE (buf) > UDP_MAX_SIZE) { GST_WARNING ("Attempting to send a UDP packet larger than maximum " "size (%d > %d)", GST_BUFFER_SIZE (buf), UDP_MAX_SIZE); } msg.msg_iov[msg.msg_iovlen].iov_len = GST_BUFFER_SIZE (buf); msg.msg_iov[msg.msg_iovlen].iov_base = GST_BUFFER_DATA (buf); msg.msg_iovlen++; size += GST_BUFFER_SIZE (buf); } 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 %d bytes", size); for (clients = sink->clients; clients; clients = g_list_next (clients)) { GstUDPClient *client; gint count; client = (GstUDPClient *) clients->data; no_clients++; GST_LOG_OBJECT (sink, "sending %d bytes to client %p", size, client); count = sink->send_duplicates ? client->refcount : 1; while (count--) { while (TRUE) { msg.msg_name = (void *) &client->theiraddr; msg.msg_namelen = sizeof (client->theiraddr); ret = sendmsg (*client->sock, &msg, 0); if (ret < 0) { if (!socket_error_is_ignorable ()) { break; } } else { num++; client->bytes_sent += ret; client->packets_sent++; sink->bytes_served += ret; break; } } } } g_mutex_unlock (sink->client_lock); g_free (iov); msg.msg_iov = NULL; GST_LOG_OBJECT (sink, "sent %d bytes to %d (of %d) clients", size, num, no_clients); } gst_buffer_list_iterator_free (it); return GST_FLOW_OK; invalid_list: gst_buffer_list_iterator_free (it); return GST_FLOW_ERROR; }
/* 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); if (sink->socket == NULL) { GST_DEBUG_OBJECT (sink, "creating sockets"); /* create sender socket try IP6, fall back to IP4 */ sink->family = G_SOCKET_FAMILY_IPV6; if ((sink->used_socket = g_socket_new (G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) { sink->family = G_SOCKET_FAMILY_IPV4; if ((sink->used_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) goto no_socket; } GST_DEBUG_OBJECT (sink, "have socket"); sink->external_socket = FALSE; } else { GST_DEBUG_OBJECT (sink, "using configured socket"); /* we use the configured socket */ sink->used_socket = G_SOCKET (g_object_ref (sink->socket)); sink->family = g_socket_get_family (sink->used_socket); sink->external_socket = TRUE; } #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. */ 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))); } } /* 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) */ 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"); } #endif g_socket_set_broadcast (sink->used_socket, TRUE); sink->bytes_to_serve = 0; sink->bytes_served = 0; gst_multiudpsink_setup_qos_dscp (sink); /* 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; } }