]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
IPTV/RTP input: handle source specific multicast (SSM) URLs in Linux
authorTom Kistner <tom@kistner.nu>
Mon, 27 Mar 2017 13:11:06 +0000 (15:11 +0200)
committerJaroslav Kysela <perex@perex.cz>
Mon, 27 Mar 2017 16:01:27 +0000 (18:01 +0200)
src/input/mpegts/iptv/iptv_udp.c
src/udp.c
src/udp.h
src/upnp.c

index 7f5f3bff2f6d14dc0e9f238c6d8babee4d39bbe8..1d61cc98d989cf9b432ebe730c8d01bc13ccc1e6 100644 (file)
@@ -40,7 +40,9 @@ iptv_udp_start ( iptv_mux_t *im, const char *raw, const url_t *url )
 
   mpegts_mux_nice_name((mpegts_mux_t*)im, name, sizeof(name));
 
-  conn = udp_bind(LS_IPTV, name, url->host, url->port,
+  /* Note: url->user is used for specifying multicast source address (SSM)
+     here. The URL format is rtp://<srcaddr>@<grpaddr>:<port> */
+  conn = udp_bind(LS_IPTV, name, url->host, url->port, url->user,
                   im->mm_iptv_interface, IPTV_BUF_SIZE, 4*1024);
   if (conn == UDP_FATAL_ERROR)
     return SM_CODE_TUNING_FAILED;
index bb607256c0ce1b14983d38360706665efff7508e..7c9f6de1de075252a225c820b3f4de5194624b62 100644 (file)
--- a/src/udp.c
+++ b/src/udp.c
@@ -119,7 +119,7 @@ udp_get_ifindex( const char *ifname )
   return r;
 }
 
-#if defined(PLATFORM_DARWIN)
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_DARWIN)
 static int
 udp_get_ifaddr( int fd, const char *ifname, struct in_addr *addr )
 {
@@ -154,7 +154,7 @@ udp_get_solip( void )
 
 udp_connection_t *
 udp_bind ( int subsystem, const char *name,
-           const char *bindaddr, int port,
+           const char *bindaddr, int port, const char *multicast_src,
            const char *ifname, int rxsize, int txsize )
 {
   int fd, ifindex, reuse = 1;
@@ -233,11 +233,48 @@ udp_bind ( int subsystem, const char *name,
       }
 #endif
 
-      if (setsockopt(fd, udp_get_solip(), IP_ADD_MEMBERSHIP, &m, sizeof(m))) {
-        inet_ntop(AF_INET, &m.imr_multiaddr, buf, sizeof(buf));
-        tvhwarn(subsystem, "%s - cannot join %s [%s]",
-                name, buf, strerror(errno));
+#if defined(PLATFORM_LINUX)
+      if (multicast_src) {
+
+        struct ip_mreq_source ms;
+        memset(&ms, 0, sizeof(ms));
+
+        ms.imr_multiaddr = IP_AS_V4(uc->ip, addr);
+
+        /* Unfortunately, ip_mreq_source does not support the ifindex parameter,
+           so we have to resolve to the ip of the interface. */
+        if (udp_get_ifaddr(fd, ifname, &ms.imr_interface) == -1) {
+          tvherror(subsystem, "%s - cannot find ip address for interface %s [e=%s]",
+                   name, ifname,  strerror(errno));
+          goto error;
+        }
+
+        if (inet_pton(AF_INET, multicast_src, &ms.imr_sourceaddr) < 1) {
+          tvherror(subsystem, "%s - invalid ipv4 address '%s' specified as multicast source [e=%s]",
+                   name, multicast_src,  strerror(errno));
+          goto error;
+        }
+
+        if (setsockopt(fd, udp_get_solip(), IP_ADD_SOURCE_MEMBERSHIP,
+                       &ms, sizeof(ms)) < 0) {
+          tvherror(subsystem, "%s - setsockopt IP_ADD_SOURCE_MEMBERSHIP failed [e=%s]",
+                   name,  strerror(errno));
+          goto error;
+        }
+
+      }
+      else {
+#endif
+        if (setsockopt(fd, udp_get_solip(), IP_ADD_MEMBERSHIP, &m, sizeof(m))) {
+          inet_ntop(AF_INET, &m.imr_multiaddr, buf, sizeof(buf));
+          tvhwarn(subsystem, "%s - cannot join %s [%s]",
+                  name, buf, strerror(errno));
+        }
+#if defined(PLATFORM_LINUX)
       }
+#endif
+
+
    }
 
   /* Bind to IPv6 group */
@@ -309,13 +346,13 @@ udp_bind_double ( udp_connection_t **_u1, udp_connection_t **_u2,
 
   memset(&ucs, 0, sizeof(ucs));
   while (1) {
-    u1 = udp_bind(subsystem, name1, host, port, ifname, rxsize1, txsize1);
+    u1 = udp_bind(subsystem, name1, host, port, NULL, ifname, rxsize1, txsize1);
     if (u1 == NULL || u1 == UDP_FATAL_ERROR)
       goto fail;
     port2 = ntohs(IP_PORT(u1->ip));
     /* RTP port should be even, RTCP port should be odd */
     if ((port2 % 2) == 0) {
-      u2 = udp_bind(subsystem, name2, host, port2 + 1, ifname, rxsize2, txsize2);
+      u2 = udp_bind(subsystem, name2, host, port2 + 1, NULL, ifname, rxsize2, txsize2);
       if (u2 != NULL && u2 != UDP_FATAL_ERROR)
         break;
     }
index bb6a5bf6be790387521d07644c109f5589d91983..7e810727cf52f84b7b9dc017b1dd1540f739e8ef 100644 (file)
--- a/src/udp.h
+++ b/src/udp.h
@@ -43,7 +43,7 @@ typedef struct udp_connection {
 
 udp_connection_t *
 udp_bind ( int subsystem, const char *name,
-           const char *bindaddr, int port,
+           const char *bindaddr, int port, const char *multicast_src,
            const char *ifname, int rxsize, int txsize );
 int
 udp_bind_double ( udp_connection_t **_u1, udp_connection_t **_u2,
index 5193b070303908daed00cd24fe0e60dac5367cda..81fef38044e769d23953a5b0113cdefb83c6612c 100644 (file)
@@ -138,11 +138,11 @@ upnp_thread( void *aux )
   int r, delay_ms;
 
   multicast = udp_bind(LS_UPNP, "upnp_thread_multicast",
-                       "239.255.255.250", 1900,
+                       "239.255.255.250", 1900, NULL,
                        NULL, 32*1024, 32*1024);
   if (multicast == NULL || multicast == UDP_FATAL_ERROR)
     goto error;
-  unicast = udp_bind(LS_UPNP, "upnp_thread_unicast", bindaddr, 0,
+  unicast = udp_bind(LS_UPNP, "upnp_thread_unicast", bindaddr, 0, NULL,
                      NULL, 32*1024, 32*1024);
   if (unicast == NULL || unicast == UDP_FATAL_ERROR)
     goto error;