From e2518fedbe8f6eb9cf1cf456381a5e350e2c4001 Mon Sep 17 00:00:00 2001 From: Jarkko Palviainen Date: Mon, 31 Aug 2009 12:13:07 +0200 Subject: udp: split out TTL and loop options Split setting the TTL and loop parameters in 2 methods as they are not related. Fix setting the TTL correctly for multicast streams. See #588245 --- gst/udp/gstmultiudpsink.c | 43 ++++++++++++++++++++++++++--- gst/udp/gstudpnetutils.c | 69 +++++++++++++++++++++++++++++++++++++---------- gst/udp/gstudpnetutils.h | 3 ++- 3 files changed, 96 insertions(+), 19 deletions(-) diff --git a/gst/udp/gstmultiudpsink.c b/gst/udp/gstmultiudpsink.c index a6afc9ae..6b5ca4d4 100644 --- a/gst/udp/gstmultiudpsink.c +++ b/gst/udp/gstmultiudpsink.c @@ -723,14 +723,26 @@ gst_multiudpsink_init_send (GstMultiUDPSink * sink) sink->bytes_to_serve = 0; sink->bytes_served = 0; - gst_udp_set_loop_ttl (sink->sock, sink->loop, sink->ttl); gst_multiudpsink_setup_qos_dscp (sink); - /* look for multicast clients and join multicast groups appropriately */ + /* 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 (sink->auto_multicast && gst_udp_is_multicast (&client->theiraddr)) - gst_udp_join_group (*(client->sock), &client->theiraddr, NULL); + if (gst_udp_is_multicast (&client->theiraddr)) { + if (sink->auto_multicast) { + if (gst_udp_join_group (*(client->sock), &client->theiraddr, NULL) + != 0) + goto join_group_failed; + } + if (gst_udp_set_loop (sink->sock, sink->loop) != 0) + goto loop_failed; + if (gst_udp_set_ttl (sink->sock, sink->ttl, TRUE) != 0) + goto ttl_failed; + } else { + if (gst_udp_set_ttl (sink->sock, sink->ttl, FALSE) != 0) + goto ttl_failed; + } } return TRUE; @@ -749,6 +761,29 @@ no_broadcast: g_strerror (errno))); return FALSE; } +join_group_failed: + { + CLOSE_IF_REQUESTED (sink); + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not join multicast group (%d): %s", errno, g_strerror (errno))); + return FALSE; + } +ttl_failed: + { + CLOSE_IF_REQUESTED (sink); + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not set TTL socket option (%d): %s", errno, + g_strerror (errno))); + return FALSE; + } +loop_failed: + { + CLOSE_IF_REQUESTED (sink); + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not set loopback socket option (%d): %s", errno, + g_strerror (errno))); + return FALSE; + } } static void diff --git a/gst/udp/gstudpnetutils.c b/gst/udp/gstudpnetutils.c index 2ed65e68..322351a8 100644 --- a/gst/udp/gstudpnetutils.c +++ b/gst/udp/gstudpnetutils.c @@ -113,7 +113,7 @@ beach: } int -gst_udp_set_loop_ttl (int sockfd, gboolean loop, int ttl) +gst_udp_set_loop (int sockfd, gboolean loop) { socklen_t socklen; struct sockaddr_storage addr; @@ -128,29 +128,70 @@ gst_udp_set_loop_ttl (int sockfd, gboolean loop, int ttl) switch (addr.ss_family) { case AF_INET: { - if ((ret = - setsockopt (sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &l, - sizeof (l))) < 0) + ret = setsockopt (sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &l, sizeof (l)); + if (ret < 0) return ret; - if ((ret = - setsockopt (sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, - sizeof (ttl))) < 0) - return ret; break; } case AF_INET6: { - if ((ret = - setsockopt (sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &l, - sizeof (l))) < 0) + ret = + setsockopt (sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &l, + sizeof (l)); + if (ret < 0) return ret; - if ((ret = - setsockopt (sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, - sizeof (ttl))) < 0) + break; + } + default: +#ifdef G_OS_WIN32 + WSASetLastError (WSAEAFNOSUPPORT); +#else + errno = EAFNOSUPPORT; +#endif + } + + return ret; +} + +int +gst_udp_set_ttl (int sockfd, int ttl, gboolean is_multicast) +{ + socklen_t socklen; + struct sockaddr_storage addr; + int optname = -1; + int ret = -1; + + socklen = sizeof (addr); + if ((ret = getsockname (sockfd, (struct sockaddr *) &addr, &socklen)) < 0) { + return ret; + } + + switch (addr.ss_family) { + case AF_INET: + { + optname = (is_multicast == TRUE) ? IP_MULTICAST_TTL : IP_TTL; + ret = setsockopt (sockfd, IPPROTO_IP, optname, &ttl, sizeof (ttl)); + if (ret < 0) + return ret; + break; + } + case AF_INET6: + { + optname = + (is_multicast == TRUE) ? IPV6_MULTICAST_HOPS : IPV6_UNICAST_HOPS; + ret = setsockopt (sockfd, IPPROTO_IPV6, optname, &ttl, sizeof (ttl)); + if (ret < 0) return ret; + /* When using IPV4 address with IPV6 socket, both TTL values + must be set in order to actually use the given value. + Has no effect when IPV6 address is used. */ + optname = (is_multicast == TRUE) ? IP_MULTICAST_TTL : IP_TTL; + ret = setsockopt (sockfd, IPPROTO_IP, optname, &ttl, sizeof (ttl)); + if (ret < 0) + return ret; break; } default: diff --git a/gst/udp/gstudpnetutils.h b/gst/udp/gstudpnetutils.h index b8f49a73..faaece90 100644 --- a/gst/udp/gstudpnetutils.h +++ b/gst/udp/gstudpnetutils.h @@ -80,7 +80,8 @@ int gst_udp_get_sockaddr_length(struct sockaddr_storage *addr); int gst_udp_get_addr (const char *hostname, int port, struct sockaddr_storage *addr); int gst_udp_is_multicast (struct sockaddr_storage *addr); -int gst_udp_set_loop_ttl (int sockfd, gboolean loop, int ttl); +int gst_udp_set_loop (int sockfd, gboolean loop); +int gst_udp_set_ttl (int sockfd, int ttl, gboolean is_multicast); int gst_udp_join_group (int sockfd, struct sockaddr_storage *addr, gchar *iface); -- cgit