[FFmpeg-cvslog] udp: Support IGMPv3 source specific multicast and source blocking

Martin Storsjö git at videolan.org
Thu Jun 28 01:09:05 CEST 2012


ffmpeg | branch: master | Martin Storsjö <martin at martin.st> | Thu Jun 21 14:19:56 2012 +0300| [75d339e044f9b87dd9aa4bdaee73b1a8323d4a15] | committer: Martin Storsjö

udp: Support IGMPv3 source specific multicast and source blocking

Based on an original patch by Stephen D'Angelo <SDAngelo at evertz.com>.

Signed-off-by: Martin Storsjö <martin at martin.st>

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=75d339e044f9b87dd9aa4bdaee73b1a8323d4a15
---

 configure          |    6 +++
 doc/protocols.texi |    8 ++++
 libavformat/udp.c  |  114 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 127 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 6f63780..3619eff 100755
--- a/configure
+++ b/configure
@@ -1136,6 +1136,8 @@ HAVE_LIST="
     strptime
     strtok_r
     struct_addrinfo
+    struct_group_source_req
+    struct_ip_mreq_source
     struct_ipv6_mreq
     struct_rusage_ru_maxrss
     struct_sockaddr_in6
@@ -2811,6 +2813,8 @@ fi
 if enabled network; then
     check_type "sys/types.h sys/socket.h" socklen_t
     check_type netdb.h "struct addrinfo"
+    check_type netinet/in.h "struct group_source_req" -D_BSD_SOURCE
+    check_type netinet/in.h "struct ip_mreq_source" -D_BSD_SOURCE
     check_type netinet/in.h "struct ipv6_mreq" -D_DARWIN_C_SOURCE
     check_type netinet/in.h "struct sockaddr_in6"
     check_type "sys/types.h sys/socket.h" "struct sockaddr_storage"
@@ -2826,6 +2830,8 @@ if enabled network; then
             network_extralibs="-lws2_32"; }
         check_type ws2tcpip.h socklen_t
         check_type ws2tcpip.h "struct addrinfo"
+        check_type ws2tcpip.h "struct group_source_req"
+        check_type ws2tcpip.h "struct ip_mreq_source"
         check_type ws2tcpip.h "struct ipv6_mreq"
         check_type ws2tcpip.h "struct sockaddr_in6"
         check_type ws2tcpip.h "struct sockaddr_storage"
diff --git a/doc/protocols.texi b/doc/protocols.texi
index e90d1b4..e75f108 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -536,6 +536,14 @@ and makes writes return with AVERROR(ECONNREFUSED) if "destination
 unreachable" is received.
 For receiving, this gives the benefit of only receiving packets from
 the specified peer address/port.
+
+ at item sources=@var{address}[, at var{address}]
+Only receive packets sent to the multicast group from one of the
+specified sender IP addresses.
+
+ at item block=@var{address}[, at var{address}]
+Ignore packets sent to the multicast group from the specified
+sender IP addresses.
 @end table
 
 Some usage examples of the udp protocol with @command{avconv} follow.
diff --git a/libavformat/udp.c b/libavformat/udp.c
index 71b445b..e848ecd 100644
--- a/libavformat/udp.c
+++ b/libavformat/udp.c
@@ -168,6 +168,79 @@ static struct addrinfo* udp_resolve_host(const char *hostname, int port,
     return res;
 }
 
+static int udp_set_multicast_sources(int sockfd, struct sockaddr *addr,
+                                     int addr_len, char **sources,
+                                     int nb_sources, int include)
+{
+#if HAVE_STRUCT_GROUP_SOURCE_REQ && defined(MCAST_BLOCK_SOURCE) && !defined(_WIN32)
+    /* These ones are available in the microsoft SDK, but don't seem to work
+     * as on linux, so just prefer the v4-only approach there for now. */
+    int i;
+    for (i = 0; i < nb_sources; i++) {
+        struct group_source_req mreqs;
+        int level = addr->sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6;
+        struct addrinfo *sourceaddr = udp_resolve_host(sources[i], 0,
+                                                       SOCK_DGRAM, AF_UNSPEC,
+                                                       AI_NUMERICHOST);
+        if (!sourceaddr)
+            return AVERROR(ENOENT);
+
+        mreqs.gsr_interface = 0;
+        memcpy(&mreqs.gsr_group, addr, addr_len);
+        memcpy(&mreqs.gsr_source, sourceaddr->ai_addr, sourceaddr->ai_addrlen);
+        freeaddrinfo(sourceaddr);
+
+        if (setsockopt(sockfd, level,
+                       include ? MCAST_JOIN_SOURCE_GROUP : MCAST_BLOCK_SOURCE,
+                       (const void *)&mreqs, sizeof(mreqs)) < 0) {
+            if (include)
+                log_net_error(NULL, AV_LOG_ERROR, "setsockopt(MCAST_JOIN_SOURCE_GROUP)");
+            else
+                log_net_error(NULL, AV_LOG_ERROR, "setsockopt(MCAST_BLOCK_SOURCE)");
+            return ff_neterrno();
+        }
+    }
+#elif HAVE_STRUCT_IP_MREQ_SOURCE && defined(IP_BLOCK_SOURCE)
+    int i;
+    if (addr->sa_family != AF_INET) {
+        av_log(NULL, AV_LOG_ERROR,
+               "Setting multicast sources only supported for IPv4\n");
+        return AVERROR(EINVAL);
+    }
+    for (i = 0; i < nb_sources; i++) {
+        struct ip_mreq_source mreqs;
+        struct addrinfo *sourceaddr = udp_resolve_host(sources[i], 0,
+                                                       SOCK_DGRAM, AF_UNSPEC,
+                                                       AI_NUMERICHOST);
+        if (!sourceaddr)
+            return AVERROR(ENOENT);
+        if (sourceaddr->ai_addr->sa_family != AF_INET) {
+            freeaddrinfo(sourceaddr);
+            av_log(NULL, AV_LOG_ERROR, "%s is of incorrect protocol family\n",
+                   sources[i]);
+            return AVERROR(EINVAL);
+        }
+
+        mreqs.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
+        mreqs.imr_interface.s_addr = INADDR_ANY;
+        mreqs.imr_sourceaddr.s_addr = ((struct sockaddr_in *)sourceaddr->ai_addr)->sin_addr.s_addr;
+        freeaddrinfo(sourceaddr);
+
+        if (setsockopt(sockfd, IPPROTO_IP,
+                       include ? IP_ADD_SOURCE_MEMBERSHIP : IP_BLOCK_SOURCE,
+                       (const void *)&mreqs, sizeof(mreqs)) < 0) {
+            if (include)
+                log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP)");
+            else
+                log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_BLOCK_SOURCE)");
+            return ff_neterrno();
+        }
+    }
+#else
+    return AVERROR(ENOSYS);
+#endif
+    return 0;
+}
 static int udp_set_url(struct sockaddr_storage *addr,
                        const char *hostname, int port)
 {
@@ -318,6 +391,8 @@ static int udp_open(URLContext *h, const char *uri, int flags)
     struct sockaddr_storage my_addr;
     int len;
     int reuse_specified = 0;
+    int i, include = 0, num_sources = 0;
+    char *sources[32];
 
     h->is_streamed = 1;
     h->max_packet_size = 1472;
@@ -355,6 +430,25 @@ static int udp_open(URLContext *h, const char *uri, int flags)
         if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) {
             av_strlcpy(localaddr, buf, sizeof(localaddr));
         }
+        if (av_find_info_tag(buf, sizeof(buf), "sources", p))
+            include = 1;
+        if (include || av_find_info_tag(buf, sizeof(buf), "block", p)) {
+            char *source_start;
+
+            source_start = buf;
+            while (1) {
+                char *next = strchr(source_start, ',');
+                if (next)
+                    *next = '\0';
+                sources[num_sources] = av_strdup(source_start);
+                if (!sources[num_sources])
+                    goto fail;
+                source_start = next + 1;
+                num_sources++;
+                if (num_sources >= FF_ARRAY_ELEMS(sources) || !next)
+                    break;
+            }
+        }
     }
 
     /* fill the dest addr */
@@ -412,8 +506,21 @@ static int udp_open(URLContext *h, const char *uri, int flags)
         }
         if (h->flags & AVIO_FLAG_READ) {
             /* input */
-            if (udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr) < 0)
+            if (num_sources == 0 || !include) {
+                if (udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr) < 0)
+                    goto fail;
+
+                if (num_sources) {
+                    if (udp_set_multicast_sources(udp_fd, (struct sockaddr *)&s->dest_addr, s->dest_addr_len, sources, num_sources, 0) < 0)
+                        goto fail;
+                }
+            } else if (include && num_sources) {
+                if (udp_set_multicast_sources(udp_fd, (struct sockaddr *)&s->dest_addr, s->dest_addr_len, sources, num_sources, 1) < 0)
+                    goto fail;
+            } else {
+                av_log(NULL, AV_LOG_ERROR, "invalid udp settings: inclusive multicast but no sources given\n");
                 goto fail;
+            }
         }
     }
 
@@ -441,11 +548,16 @@ static int udp_open(URLContext *h, const char *uri, int flags)
         }
     }
 
+    for (i = 0; i < num_sources; i++)
+        av_free(sources[i]);
+
     s->udp_fd = udp_fd;
     return 0;
  fail:
     if (udp_fd >= 0)
         closesocket(udp_fd);
+    for (i = 0; i < num_sources; i++)
+        av_free(sources[i]);
     return AVERROR(EIO);
 }
 



More information about the ffmpeg-cvslog mailing list