[FFmpeg-devel] [PATCH] rtsp: proper implementation of RFC4570

Ed Torbett ed.torbett at simulation-systems.co.uk
Fri Jul 26 12:38:00 CEST 2013


RFC4570 introduces the source-filter attribute to SDP files.
This patch fully implements this SDP file attribute according
to the syntax defined within the RFC.

Signed-off-by: Edward Torbett <ed.torbett at simulation-systems.co.uk>
---
 libavformat/rtpproto.c | 135 ++++++++++++++++++++++++++++++++++++++-----------
 libavformat/rtsp.c     | 122 ++++++++++++++++++++++++++++++++++++++++----
 libavformat/rtsp.h     |   9 +++-
 libavformat/udp.c      |  71 ++++++++++++++++----------
 4 files changed, 270 insertions(+), 67 deletions(-)

diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c
index 33affc3..53ad6d0 100644
--- a/libavformat/rtpproto.c
+++ b/libavformat/rtpproto.c
@@ -42,8 +42,8 @@
 
 typedef struct RTPContext {
     URLContext *rtp_hd, *rtcp_hd;
-    int rtp_fd, rtcp_fd, ssm;
-    struct sockaddr_storage ssm_addr;
+    int rtp_fd, rtcp_fd, nb_ssm_include_addrs, nb_ssm_exclude_addrs;
+    struct sockaddr_storage **ssm_include_addrs, **ssm_exclude_addrs;
 } RTPContext;
 
 /**
@@ -95,13 +95,13 @@ static struct addrinfo* rtp_resolve_host(const char *hostname, int port,
     return res;
 }
 
-static int compare_addr(const struct sockaddr_storage *a,
+static int rtp_compare_addr(const struct sockaddr_storage *a,
                         const struct sockaddr_storage *b)
 {
     if (a->ss_family != b->ss_family)
-        return 1;
+        return 0;
     if (a->ss_family == AF_INET) {
-        return (((const struct sockaddr_in *)a)->sin_addr.s_addr !=
+        return (((const struct sockaddr_in *)a)->sin_addr.s_addr ==
                 ((const struct sockaddr_in *)b)->sin_addr.s_addr);
     }
 
@@ -109,10 +109,30 @@ static int compare_addr(const struct sockaddr_storage *a,
     if (a->ss_family == AF_INET6) {
         const uint8_t *s6_addr_a = ((const struct sockaddr_in6 *)a)->sin6_addr.s6_addr;
         const uint8_t *s6_addr_b = ((const struct sockaddr_in6 *)b)->sin6_addr.s6_addr;
-        return memcmp(s6_addr_a, s6_addr_b, 16);
+        return (0 == memcmp(s6_addr_a, s6_addr_b, 16));
     }
 #endif
-    return 1;
+    return 0;
+}
+
+static int rtp_check_source_lists(RTPContext *s, struct sockaddr_storage *source_addr_ptr) {
+    int i;
+    if (s->nb_ssm_exclude_addrs) {
+        for (i = 0; i < s->nb_ssm_exclude_addrs; i++) {
+            if (rtp_compare_addr(source_addr_ptr, s->ssm_exclude_addrs[i]))
+                return 1;
+        }
+    }
+    if (s->nb_ssm_include_addrs) {
+        int no_match = 1;
+        for (i = 0; no_match && i < s->nb_ssm_include_addrs; i++) {
+            if (rtp_compare_addr(source_addr_ptr, s->ssm_include_addrs[i]))
+                no_match = 0;
+        }
+        if (no_match)
+            return 1;
+    }
+    return 0;
 }
 
 /**
@@ -139,7 +159,8 @@ static void build_udp_url(char *buf, int buf_size,
                           const char *hostname, int port,
                           int local_port, int ttl,
                           int max_packet_size, int connect,
-                          const char* sources)
+                          const char* include_sources,
+                          const char* exclude_sources)
 {
     ff_url_join(buf, buf_size, "udp", NULL, hostname, port, NULL);
     if (local_port >= 0)
@@ -151,8 +172,10 @@ static void build_udp_url(char *buf, int buf_size,
     if (connect)
         url_add_option(buf, buf_size, "connect=1");
     url_add_option(buf, buf_size, "fifo_size=0");
-    if (sources && sources[0])
-        url_add_option(buf, buf_size, "sources=%s", sources);
+    if (include_sources && include_sources[0])
+        url_add_option(buf, buf_size, "sources=%s", include_sources);
+    if (exclude_sources && exclude_sources[0])
+        url_add_option(buf, buf_size, "block=%s", exclude_sources);
 }
 
 /**
@@ -173,17 +196,56 @@ static void build_udp_url(char *buf, int buf_size,
  * if the local rtcp port is not set it will be the local rtp port + 1
  */
 
+static void rtp_parse_addr_list(char *buf, struct sockaddr_storage ***address_list_ptr, int *address_list_size_ptr) {
+    struct addrinfo *sourceaddr = NULL;
+    struct sockaddr_storage *source_addr;
+    char tmp = '\0', *p = buf, *next;
+
+    /* Resolve all of the IPs */
+            
+    while (p && p[0]) {
+        next = strchr(p, ',');
+
+        if (next) {
+            tmp = *next;
+            *next = '\0';
+        }
+
+        source_addr = av_mallocz(sizeof(struct sockaddr_storage));
+        if (!source_addr)
+            break;
+        
+        sourceaddr = rtp_resolve_host(p, 0,
+                        SOCK_DGRAM, AF_UNSPEC,
+                        0);
+                
+        memcpy(source_addr, sourceaddr->ai_addr, sourceaddr->ai_addrlen);
+        freeaddrinfo(sourceaddr);
+
+        dynarray_add(address_list_ptr, address_list_size_ptr, source_addr);
+   
+        if (next) {
+            *next = tmp;
+            p = next + 1;
+        } else {
+            p = 0;
+        }
+    }
+}
+
 static int rtp_open(URLContext *h, const char *uri, int flags)
 {
     RTPContext *s = h->priv_data;
     int rtp_port, rtcp_port,
         ttl, connect,
         local_rtp_port, local_rtcp_port, max_packet_size;
-    char hostname[256], sources[1024] = "";
+    char hostname[256], include_sources[1024] = "", exclude_sources[1024] = "";
     char buf[1024];
     char path[1024];
     const char *p;
 
+    av_log(h, AV_LOG_DEBUG, "Opening URL: %s\n", uri);
+
     av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port,
                  path, sizeof(path), uri);
     /* extract parameters */
@@ -218,26 +280,18 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
             connect = strtol(buf, NULL, 10);
         }
         if (av_find_info_tag(buf, sizeof(buf), "sources", p)) {
-            struct addrinfo *sourceaddr = NULL;
-            av_strlcpy(sources, buf, sizeof(sources));
-
-            /* Try resolving the IP if only one IP is specified - we don't
-             * support manually checking more than one IP. */
-            if (!strchr(sources, ','))
-                sourceaddr = rtp_resolve_host(sources, 0,
-                                              SOCK_DGRAM, AF_UNSPEC,
-                                              AI_NUMERICHOST);
-            if (sourceaddr) {
-                s->ssm = 1;
-                memcpy(&s->ssm_addr, sourceaddr->ai_addr, sourceaddr->ai_addrlen);
-                freeaddrinfo(sourceaddr);
-            }
+            av_strlcpy(include_sources, buf, sizeof(include_sources));
+            rtp_parse_addr_list(buf, &s->ssm_include_addrs, &s->nb_ssm_include_addrs);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "block", p)) {
+            av_strlcpy(exclude_sources, buf, sizeof(exclude_sources));
+            rtp_parse_addr_list(buf, &s->ssm_exclude_addrs, &s->nb_ssm_exclude_addrs);
         }
     }
 
     build_udp_url(buf, sizeof(buf),
                   hostname, rtp_port, local_rtp_port, ttl, max_packet_size,
-                  connect, sources);
+                  connect, include_sources, exclude_sources);
     if (ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL) < 0)
         goto fail;
     if (local_rtp_port>=0 && local_rtcp_port<0)
@@ -245,7 +299,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
 
     build_udp_url(buf, sizeof(buf),
                   hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size,
-                  connect, sources);
+                  connect, include_sources, exclude_sources);
     if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL) < 0)
         goto fail;
 
@@ -291,8 +345,9 @@ static int rtp_read(URLContext *h, uint8_t *buf, int size)
                         continue;
                     return AVERROR(EIO);
                 }
-                if (s->ssm && compare_addr(&from, &s->ssm_addr))
+                if (rtp_check_source_lists(s, &from))
                     continue;
+
                 break;
             }
             /* then RTP */
@@ -306,8 +361,9 @@ static int rtp_read(URLContext *h, uint8_t *buf, int size)
                         continue;
                     return AVERROR(EIO);
                 }
-                if (s->ssm && compare_addr(&from, &s->ssm_addr))
+                if (rtp_check_source_lists(s, &from))
                     continue;
+                
                 break;
             }
         } else if (n < 0) {
@@ -340,6 +396,27 @@ static int rtp_write(URLContext *h, const uint8_t *buf, int size)
 static int rtp_close(URLContext *h)
 {
     RTPContext *s = h->priv_data;
+    struct sockaddr_storage *source_addr;
+    int i;
+    if (s->nb_ssm_include_addrs) {
+        for (i = 0; i < s->nb_ssm_include_addrs; i++) {
+            source_addr = s->ssm_include_addrs[i];
+            if (source_addr)
+                av_free(source_addr);
+        }
+        av_freep(&s->ssm_include_addrs);
+        s->nb_ssm_include_addrs = 0;
+    }
+
+    if (s->nb_ssm_exclude_addrs) {
+        for (i = 0; i < s->nb_ssm_exclude_addrs; i++) {
+            source_addr = s->ssm_exclude_addrs[i];
+            if (source_addr)
+                av_free(source_addr);
+        }
+        av_freep(&s->ssm_exclude_addrs);
+        s->nb_ssm_exclude_addrs = 0;
+    }
 
     ffurl_close(s->rtp_hd);
     ffurl_close(s->rtcp_hd);
diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c
index af574f1..ddc7d88 100644
--- a/libavformat/rtsp.c
+++ b/libavformat/rtsp.c
@@ -289,6 +289,10 @@ typedef struct SDPParseState {
     struct sockaddr_storage default_ip;
     int            default_ttl;
     int            skip_media;  ///< set if an unknown m= line occurs
+    int nb_default_include_source_addrs; /**< Number of source-specific multicast include source IP address (from SDP content) */
+    struct RTSPSource **default_include_source_addrs; /**< Source-specific multicast include source IP address (from SDP content) */
+    int nb_default_exclude_source_addrs; /**< Number of source-specific multicast exclude source IP address (from SDP content) */
+    struct RTSPSource **default_exclude_source_addrs; /**< Source-specific multicast exclude source IP address (from SDP content) */
 } SDPParseState;
 
 static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
@@ -301,6 +305,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
     int payload_type, i;
     AVStream *st;
     RTSPStream *rtsp_st;
+    RTSPSource *rtsp_src, *rtsp_src2;
     struct sockaddr_storage sdp_ip;
     int ttl;
 
@@ -369,6 +374,21 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
         rtsp_st->sdp_ip = s1->default_ip;
         rtsp_st->sdp_ttl = s1->default_ttl;
 
+        for (i = 0; i < s1->nb_default_include_source_addrs; i++) {
+            rtsp_src = s1->default_include_source_addrs[i];
+            if (rtsp_src) {
+                rtsp_src2 = av_memdup(rtsp_src, sizeof(RTSPSource));
+                dynarray_add(&rtsp_st->include_source_addrs, &rtsp_st->nb_include_source_addrs, rtsp_src2);
+            }
+        }
+        for (i = 0; i < s1->nb_default_exclude_source_addrs; i++) {
+            rtsp_src = s1->default_exclude_source_addrs[i];
+            if (rtsp_src) {
+                rtsp_src2 = av_memdup(rtsp_src, sizeof(RTSPSource));
+                dynarray_add(&rtsp_st->exclude_source_addrs, &rtsp_st->nb_exclude_source_addrs, rtsp_src2);
+            }
+        }
+
         get_word(buf1, sizeof(buf1), &p); /* port */
         rtsp_st->sdp_port = atoi(buf1);
 
@@ -498,22 +518,41 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
             p += strspn(p, SPACE_CHARS);
             if (av_strstart(p, "inline:", &p))
                 get_word(rtsp_st->crypto_params, sizeof(rtsp_st->crypto_params), &p);
-        } else if (av_strstart(p, "source-filter:", &p) && s->nb_streams > 0) {
+        } else if (av_strstart(p, "source-filter:", &p)) {
+            int exclude = 0;
             get_word(buf1, sizeof(buf1), &p);
-            if (strcmp(buf1, "incl"))
+            if(strcmp(buf1, "incl") && strcmp(buf1, "excl"))
                 return;
+            exclude = (0 == strcmp(buf1, "excl"));
 
             get_word(buf1, sizeof(buf1), &p);
             if (strcmp(buf1, "IN") != 0)
                 return;
             get_word(buf1, sizeof(buf1), &p);
-            if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6"))
+            if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6") && strcmp(buf1, "*"))
                 return;
-            // not checking that the destination address actually matches
+            // not checking that the destination address actually matches or is wildcard
             get_word(buf1, sizeof(buf1), &p);
 
-            rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1];
-            get_word(rtsp_st->source_addr, sizeof(rtsp_st->source_addr), &p);
+            while (*p != '\0') {
+                rtsp_src = av_mallocz(sizeof(RTSPSource));
+                get_word(rtsp_src->addr, sizeof(rtsp_src->addr), &p);
+                if (exclude) {
+                    if (s->nb_streams == 0) {
+                        dynarray_add(&s1->default_exclude_source_addrs, &s1->nb_default_exclude_source_addrs, rtsp_src);
+                    } else {
+                        rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1];           
+                        dynarray_add(&rtsp_st->exclude_source_addrs, &rtsp_st->nb_exclude_source_addrs, rtsp_src);
+                    }                    
+                } else {
+                    if (s->nb_streams == 0) {
+                        dynarray_add(&s1->default_include_source_addrs, &s1->nb_default_include_source_addrs, rtsp_src);
+                    } else {
+                        rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1];           
+                        dynarray_add(&rtsp_st->include_source_addrs, &rtsp_st->nb_include_source_addrs, rtsp_src);
+                    }                    
+                }
+            }
         } else {
             if (rt->server_type == RTSP_SERVER_WMS)
                 ff_wms_parse_sdp_a_line(s, p);
@@ -538,7 +577,7 @@ int ff_sdp_parse(AVFormatContext *s, const char *content)
 {
     RTSPState *rt = s->priv_data;
     const char *p;
-    int letter;
+    int letter, i;
     /* Some SDP lines, particularly for Realmedia or ASF RTSP streams,
      * contain long SDP lines containing complete ASF Headers (several
      * kB) or arrays of MDPR (RM stream descriptor) headers plus
@@ -549,6 +588,7 @@ int ff_sdp_parse(AVFormatContext *s, const char *content)
      * in rtpdec_xiph.c. */
     char buf[16384], *q;
     SDPParseState sdp_parse_state = { { 0 } }, *s1 = &sdp_parse_state;
+    RTSPSource *rtsp_src;
 
     p = content;
     for (;;) {
@@ -575,6 +615,28 @@ int ff_sdp_parse(AVFormatContext *s, const char *content)
         if (*p == '\n')
             p++;
     }
+
+    if (s1->nb_default_include_source_addrs) {
+        for (i = 0; i < s1->nb_default_include_source_addrs; i++) {
+            rtsp_src = s1->default_include_source_addrs[i];
+            if (rtsp_src) {
+                av_free(rtsp_src);
+            }
+        }
+        av_freep(&s1->default_include_source_addrs);
+        s1->nb_default_include_source_addrs = 0;
+    }
+    if (s1->nb_default_exclude_source_addrs) {
+        for (i = 0; i < s1->nb_default_exclude_source_addrs; i++) {
+            rtsp_src = s1->default_exclude_source_addrs[i];
+            if (rtsp_src) {
+                av_free(rtsp_src);
+            }
+        }
+        av_freep(&s1->default_exclude_source_addrs);
+        s1->nb_default_exclude_source_addrs = 0;
+    }
+
     rt->p = av_malloc(sizeof(struct pollfd)*2*(rt->nb_rtsp_streams+1));
     if (!rt->p) return AVERROR(ENOMEM);
     return 0;
@@ -618,8 +680,9 @@ void ff_rtsp_undo_setup(AVFormatContext *s)
 void ff_rtsp_close_streams(AVFormatContext *s)
 {
     RTSPState *rt = s->priv_data;
-    int i;
+    int i, j;
     RTSPStream *rtsp_st;
+    RTSPSource *rtsp_src;
 
     ff_rtsp_undo_setup(s);
     for (i = 0; i < rt->nb_rtsp_streams; i++) {
@@ -628,6 +691,25 @@ void ff_rtsp_close_streams(AVFormatContext *s)
             if (rtsp_st->dynamic_handler && rtsp_st->dynamic_protocol_context)
                 rtsp_st->dynamic_handler->free(
                     rtsp_st->dynamic_protocol_context);
+            if (rtsp_st->nb_include_source_addrs) {
+                for (j = 0; j < rtsp_st->nb_include_source_addrs; j++) {
+                    rtsp_src = rtsp_st->include_source_addrs[j];
+                    if (rtsp_src)
+                        av_free(rtsp_src);
+                }
+                av_freep(&rtsp_st->include_source_addrs);
+                rtsp_st->nb_include_source_addrs = 0;
+            }
+            if (rtsp_st->nb_exclude_source_addrs) {
+                for (j = 0; j < rtsp_st->nb_exclude_source_addrs; j++) {
+                    rtsp_src = rtsp_st->exclude_source_addrs[j];
+                    if (rtsp_src)
+                        av_free(rtsp_src);
+                }
+                av_freep(&rtsp_st->exclude_source_addrs);
+                rtsp_st->nb_exclude_source_addrs = 0;
+            }
+
             av_free(rtsp_st);
         }
     }
@@ -2061,9 +2143,10 @@ static int sdp_read_header(AVFormatContext *s)
 {
     RTSPState *rt = s->priv_data;
     RTSPStream *rtsp_st;
-    int size, i, err;
+    int size, i, j, err;
     char *content;
     char url[1024];
+    RTSPSource *rtsp_src;
 
     if (!ff_network_init())
         return AVERROR(EIO);
@@ -2100,8 +2183,25 @@ static int sdp_read_header(AVFormatContext *s)
                         "?localport=%d&ttl=%d&connect=%d", rtsp_st->sdp_port,
                         rtsp_st->sdp_ttl,
                         rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0);
-            if (rtsp_st->source_addr[0])
-                av_strlcatf(url, sizeof(url), "&sources=%s", rtsp_st->source_addr);
+
+            if (rtsp_st->nb_include_source_addrs) {
+                rtsp_src = rtsp_st->include_source_addrs[0];
+                av_strlcatf(url, sizeof(url), "&sources=%s", rtsp_src->addr);
+                for (j = 1; j < rtsp_st->nb_include_source_addrs; j++) {
+                    rtsp_src = rtsp_st->include_source_addrs[j];
+                    if (rtsp_src)
+                        av_strlcatf(url, sizeof(url), ",%s", rtsp_src->addr);
+                }
+            }
+            if (rtsp_st->nb_exclude_source_addrs) {
+                rtsp_src = rtsp_st->exclude_source_addrs[0];
+                av_strlcatf(url, sizeof(url), "&block=%s", rtsp_src->addr);
+                for (j = 1; j < rtsp_st->nb_exclude_source_addrs; j++) {
+                    rtsp_src = rtsp_st->exclude_source_addrs[j];
+                    if (rtsp_src)
+                        av_strlcatf(url, sizeof(url), ",%s", rtsp_src->addr);
+                }
+            }
             if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
                            &s->interrupt_callback, NULL) < 0) {
                 err = AVERROR_INVALIDDATA;
diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h
index e4b1cd6..cdb9848 100644
--- a/libavformat/rtsp.h
+++ b/libavformat/rtsp.h
@@ -412,6 +412,10 @@ typedef struct RTSPState {
 #define RTSP_FLAG_LISTEN      0x2    /**< Wait for incoming connections. */
 #define RTSP_FLAG_CUSTOM_IO   0x4    /**< Do all IO via the AVIOContext. */
 
+typedef struct RTSPSource {
+    char addr[128]; /**< Source-specific multicast include source IP address (from SDP content) */
+} RTSPSource;
+
 /**
  * Describe a single stream, as identified by a single m= line block in the
  * SDP content. In the case of RDT, one RTSPStream can represent multiple
@@ -435,7 +439,10 @@ typedef struct RTSPStream {
     //@{
     int sdp_port;             /**< port (from SDP content) */
     struct sockaddr_storage sdp_ip; /**< IP address (from SDP content) */
-    char source_addr[100];    /**< Source-specific multicast source IP address (from SDP content) */
+    int nb_include_source_addrs; /**< Number of source-specific multicast include source IP address (from SDP content) */
+    struct RTSPSource **include_source_addrs; /**< Source-specific multicast include source IP address (from SDP content) */
+    int nb_exclude_source_addrs; /**< Number of source-specific multicast exclude source IP address (from SDP content) */
+    struct RTSPSource **exclude_source_addrs; /**< Source-specific multicast exclude source IP address (from SDP content) */
     int sdp_ttl;              /**< IP Time-To-Live (from SDP content) */
     int sdp_payload_type;     /**< payload type */
     //@}
diff --git a/libavformat/udp.c b/libavformat/udp.c
index 744b990..259043f 100644
--- a/libavformat/udp.c
+++ b/libavformat/udp.c
@@ -237,7 +237,7 @@ static int udp_set_multicast_sources(int sockfd, struct sockaddr *addr,
         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);
+                                                       0);
         if (!sourceaddr)
             return AVERROR(ENOENT);
 
@@ -267,7 +267,7 @@ static int udp_set_multicast_sources(int sockfd, struct sockaddr *addr,
         struct ip_mreq_source mreqs;
         struct addrinfo *sourceaddr = udp_resolve_host(sources[i], 0,
                                                        SOCK_DGRAM, AF_UNSPEC,
-                                                       AI_NUMERICHOST);
+                                                       0);
         if (!sourceaddr)
             return AVERROR(ENOENT);
         if (sourceaddr->ai_addr->sa_family != AF_INET) {
@@ -507,8 +507,8 @@ static int udp_open(URLContext *h, const char *uri, int flags)
     struct sockaddr_storage my_addr;
     socklen_t len;
     int reuse_specified = 0;
-    int i, include = 0, num_sources = 0;
-    char *sources[32];
+    int i, num_include_sources = 0, num_exclude_sources = 0;
+    char *include_sources[32], *exclude_sources[32];
 
     h->is_streamed = 1;
 
@@ -562,9 +562,7 @@ 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)) {
+        if (av_find_info_tag(buf, sizeof(buf), "sources", p)) {
             char *source_start;
 
             source_start = buf;
@@ -572,12 +570,29 @@ static int udp_open(URLContext *h, const char *uri, int flags)
                 char *next = strchr(source_start, ',');
                 if (next)
                     *next = '\0';
-                sources[num_sources] = av_strdup(source_start);
-                if (!sources[num_sources])
+                include_sources[num_include_sources] = av_strdup(source_start);
+                if (!include_sources[num_include_sources])
                     goto fail;
                 source_start = next + 1;
-                num_sources++;
-                if (num_sources >= FF_ARRAY_ELEMS(sources) || !next)
+                num_include_sources++;
+                if (num_include_sources >= FF_ARRAY_ELEMS(include_sources) || !next)
+                    break;
+            }
+         }
+        if (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';
+                exclude_sources[num_exclude_sources] = av_strdup(source_start);
+                if (!exclude_sources[num_exclude_sources])
+                    goto fail;
+                source_start = next + 1;
+                num_exclude_sources++;
+                if (num_exclude_sources >= FF_ARRAY_ELEMS(exclude_sources) || !next)
                     break;
             }
         }
@@ -648,20 +663,20 @@ static int udp_open(URLContext *h, const char *uri, int flags)
         }
         if (h->flags & AVIO_FLAG_READ) {
             /* input */
-            if (num_sources == 0 || !include) {
+            if (num_include_sources && num_exclude_sources) {
+                av_log(h, AV_LOG_ERROR, "Simultaneously including and excluding multicast sources is not supported\n");
+                goto fail;
+            }
+            if (num_include_sources) {
+                if (udp_set_multicast_sources(udp_fd, (struct sockaddr *)&s->dest_addr, s->dest_addr_len, include_sources, num_include_sources, 1) < 0)
+                    goto fail;
+            } else {
                 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)
+            }
+            if (num_exclude_sources) {
+                if (udp_set_multicast_sources(udp_fd, (struct sockaddr *)&s->dest_addr, s->dest_addr_len, exclude_sources, num_exclude_sources, 0) < 0)
                     goto fail;
-            } else {
-                av_log(NULL, AV_LOG_ERROR, "invalid udp settings: inclusive multicast but no sources given\n");
-                goto fail;
             }
         }
     }
@@ -690,8 +705,10 @@ static int udp_open(URLContext *h, const char *uri, int flags)
         }
     }
 
-    for (i = 0; i < num_sources; i++)
-        av_freep(&sources[i]);
+    for (i = 0; i < num_include_sources; i++)
+        av_freep(&include_sources[i]);
+    for (i = 0; i < num_exclude_sources; i++)
+        av_freep(&exclude_sources[i]);
 
     s->udp_fd = udp_fd;
 
@@ -731,8 +748,10 @@ static int udp_open(URLContext *h, const char *uri, int flags)
     if (udp_fd >= 0)
         closesocket(udp_fd);
     av_fifo_free(s->fifo);
-    for (i = 0; i < num_sources; i++)
-        av_freep(&sources[i]);
+    for (i = 0; i < num_include_sources; i++)
+        av_freep(&include_sources[i]);
+    for (i = 0; i < num_exclude_sources; i++)
+        av_freep(&exclude_sources[i]);
     return AVERROR(EIO);
 }
 
-- 
1.8.3.3





More information about the ffmpeg-devel mailing list