diff --git a/src/input/mpegts/iptv/iptv.c b/src/input/mpegts/iptv/iptv.c index bad1cbab..6d1621e8 100644 --- a/src/input/mpegts/iptv/iptv.c +++ b/src/input/mpegts/iptv/iptv.c @@ -20,11 +20,28 @@ #include "iptv_private.h" #include "tcp.h" -#include +#include +#include +#include #include +#include #include +#include #include #include +#include +#include +#include +#include +#include +#include + +#if defined(PLATFORM_FREEBSD) +# ifndef IPV6_ADD_MEMBERSHIP +# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +# endif +#endif /* * Globals @@ -41,46 +58,47 @@ pthread_mutex_t iptv_lock; typedef struct url { - char buf[2048]; - const char *scheme; - const char *host; - uint16_t port; - const char *path; + char scheme[16]; + char user[128]; + char pass[128]; + char host[256]; + uint16_t port; + char path[256]; } url_t; -static int -url_parse ( url_t *up, const char *urlstr ) +#define UC "[a-z0-9_\\-\\.!£$%^&]" +#define PC UC +#define HC "[a-z0-9\\-\\.]" +#define URL_RE "^(\\w+)://(("UC"+)(:("PC"+))?@)?("HC"+)(:([0-9]+))?(/.*)?" + +regex_t urlre; + +static int +urlparse ( const char *str, url_t *url ) { - char *t1, *t2; - strcpy(up->buf, urlstr); - - /* Scheme */ - up->scheme = t1 = up->buf; - if (!(t2 = strstr(t1, "://"))) { + regmatch_t m[12]; + char buf[16]; + + memset(m, 0, sizeof(m)); + + if (regexec(&urlre, str, 12, m, 0)) return 1; - } - *t2 = 0; - - /* Host */ - up->host = t1 = t2 + 3; - up->path = NULL; - if ((t2 = strstr(t1, "/"))) { - *t2 = 0; - up->path = t2 + 1; - } - - /* Port */ - up->port = 0; - if (!(t2 = strstr(up->host, ":"))) { - if (!strcmp(up->scheme, "https")) - up->port = 443; - else if (!strcmp(up->scheme, "http")) - up->port = 80; - } else { - *t2 = 0; - up->port = atoi(t2+1); - } - + +#define copy(x, i)\ + {\ + int len = m[i].rm_eo - m[i].rm_so;\ + if (len >= sizeof(x) - 1)\ + len = sizeof(x) - 1;\ + memcpy(x, str+m[i].rm_so, len);\ + x[len] = 0;\ + }(void)0 + copy(url->scheme, 1); + copy(url->user, 3); + copy(url->pass, 5); + copy(url->host, 6); + copy(url->path, 9); + copy(buf, 8); + url->port = atoi(buf); return 0; } @@ -93,7 +111,7 @@ static int http_connect (const char *urlstr) int fd, c, i; char buf[1024]; url_t url; - if (url_parse(&url, urlstr)) + if (urlparse(urlstr, &url)) return -1; /* Make connection */ @@ -152,35 +170,149 @@ const idclass_t iptv_input_class = { * HTTP */ static int -iptv_input_start_http ( iptv_mux_t *im ) +iptv_input_start_http + ( iptv_mux_t *im, url_t *url, const char *name ) { /* Setup connection */ - im->mm_iptv_fd = http_connect(im->mm_iptv_url); + return http_connect("TODO"); +} - return im->mm_iptv_fd <= 0 ? SM_CODE_TUNING_FAILED : 0; +/* + * UDP + * + * TODO: add IPv6 support + */ +static int +iptv_input_start_udp + ( iptv_mux_t *im, url_t *url, const char *name ) +{ + int fd, solip, rxsize, reuse; + struct ifreq ifr; + struct ip_mreqn m; + struct addrinfo *addr; + struct sockaddr_in sin; + + /* Open socket */ + if ((fd = tvh_socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + tvherror("iptv", "%s - failed to create socket [%s]", + name, strerror(errno)); + return -1; + } + + /* Bind to interface */ + if (im->mm_iptv_interface && *im->mm_iptv_interface) { + printf("have interface\n"); + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", im->mm_iptv_interface); + if (ioctl(fd, SIOCGIFINDEX, &ifr)) { + tvherror("iptv", "%s - could not find interface %s", + name, im->mm_iptv_interface); + goto error; + } + } + + /* Lookup hostname */ + if (getaddrinfo(url->host, NULL, NULL, &addr)) { + tvherror("iptv", "%s - failed to lookup host %s", name, url->host); + return -1; + } + + /* Bind to address */ + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(url->port); // TODO: default? + sin.sin_addr.s_addr = ((struct sockaddr_in*)addr->ai_addr)->sin_addr.s_addr; + freeaddrinfo(addr); + reuse = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + tvherror("iptv", "%s - cannot bind %s:%d [%s]", + name, inet_ntoa(sin.sin_addr), 0, strerror(errno)); + goto error; + } + + + /* Join IPv4 group */ + memset(&m, 0, sizeof(m)); + m.imr_multiaddr.s_addr = sin.sin_addr.s_addr; + m.imr_address.s_addr = 0; +#if defined(PLATFORM_LINUX) + m.imr_ifindex = ifr.ifr_ifindex; +#elif defined(PLATFORM_FREEBSD) + m.imr_ifindex = ifr.ifr_index; +#endif + +#ifdef SOL_IP + solip = SOL_IP; +#else + { + struct protoent *pent; + pent = getprotobyname("ip"); + solip = (pent != NULL) ? pent->p_proto : 0; + } +#endif + + if (setsockopt(fd, solip, IP_ADD_MEMBERSHIP, &m, + sizeof(struct ip_mreqn)) == -1) { + tvherror("iptv", "%s - cannot join %s [%s]", + name, inet_ntoa(m.imr_multiaddr), strerror(errno)); + goto error; + } + + /* Increase RX buffer size */ + rxsize = 65536;//262142; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rxsize, sizeof(rxsize)) == -1) + tvhwarn("iptv", "%s - cannot increase UDP rx buffer size [%s]", + name, strerror(errno)); + + /* Done */ + return fd; + +error: + close(fd); + return -1; } static int iptv_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) { - int ret = SM_CODE_TUNING_FAILED; + int fd, ret = SM_CODE_TUNING_FAILED; iptv_mux_t *im = (iptv_mux_t*)mmi->mmi_mux; assert(mmi == &im->mm_iptv_instance); + char buf[256]; + url_t url; + + im->mm_display_name((mpegts_mux_t*)im, buf, sizeof(buf)); + if (urlparse(im->mm_iptv_url ?: "", &url)) { + tvherror("iptv", "%s - invalid URL [%s]", buf, im->mm_iptv_url); + return ret; + } pthread_mutex_lock(&iptv_lock); if (!im->mm_active) { /* HTTP */ // TODO: this needs to happen in a thread - if (!strncmp(im->mm_iptv_url, "http", 4)) - ret = iptv_input_start_http(im); + if (!strcmp("http", url.scheme)) + fd = iptv_input_start_http(im, &url, buf); + + /* UDP/RTP (both mcast) */ + else if (!strcmp(url.scheme, "udp") || + !strcmp(url.scheme, "rtp")) + fd = iptv_input_start_udp(im, &url, buf); + + /* Unknown */ + else { + tvherror("iptv", "%s - invalid scheme [%s]", buf, url.scheme); + fd = -1; + } /* OK */ - if (!ret) { + if (fd != -1) { struct epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; - ev.data.fd = im->mm_iptv_fd; + ev.data.fd = im->mm_iptv_fd = fd; epoll_ctl(iptv_poll_fd, EPOLL_CTL_ADD, im->mm_iptv_fd, &ev); im->mm_active = mmi; @@ -190,6 +322,7 @@ iptv_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) MT_QUICKREQ| MT_CRC, DVB_PAT_PID); // TODO: need to fire mux start event + ret = 0; } } pthread_mutex_unlock(&iptv_lock); @@ -233,11 +366,10 @@ iptv_input_display_name ( mpegts_input_t *mi, char *buf, size_t len ) snprintf(buf, len, "IPTV"); } - static void * iptv_input_thread ( void *aux ) { - int nfds, fd, r, pos = 0; + int hlen, nfds, fd, r, pos = 0; uint8_t tsb[65536]; struct epoll_event ev; iptv_mux_t *im; @@ -278,13 +410,33 @@ iptv_input_thread ( void *aux ) } im = (iptv_mux_t*)mm; - /* Raw TS */ - if (!strncmp(im->mm_iptv_url, "http", 4)) { - pos = mpegts_input_recv_packets((mpegts_input_t*)&iptv_input, - &im->mm_iptv_instance, - tsb, r, NULL, NULL, "iptv"); + /* RTP */ + hlen = 0; + if (!strncmp(im->mm_iptv_url, "rtp", 3)) { + if (r < 12) + goto done; + if ((tsb[0] & 0xC0) != 0x80) + goto done; + if ((tsb[1] & 0x7F) != 33) + goto done; + hlen = ((tsb[0] & 0xf) * 4) + 12; + if (tsb[0] & 0x10) { + if (r < hlen+4) + goto done; + hlen += (tsb[hlen+2] << 8) | (tsb[hlen+3]*4); + hlen += 4; + } + if (r < hlen || ((r - hlen) % 188) != 0) + goto done; } + /* TS */ + pos = mpegts_input_recv_packets((mpegts_input_t*)&iptv_input, + &im->mm_iptv_instance, + tsb+hlen, r-hlen, NULL, NULL, "iptv"); +if(pos) +printf("pos = %d\n", pos); +done: pthread_mutex_unlock(&iptv_lock); } return NULL; @@ -310,6 +462,13 @@ iptv_network_create_mux return NULL; } +static mpegts_mux_t * +iptv_network_create_mux2 + ( mpegts_network_t *mm, htsmsg_t *conf ) +{ + return (mpegts_mux_t*)iptv_mux_create(NULL, conf); +} + static mpegts_service_t * iptv_network_create_service ( mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid ) @@ -318,12 +477,25 @@ iptv_network_create_service iptv_service_create(NULL, (iptv_mux_t*)mm, sid, pmt_pid); } +static const idclass_t * +iptv_network_mux_class ( mpegts_network_t *mm ) +{ + extern const idclass_t iptv_mux_class; + return &iptv_mux_class; +} + /* * Intialise and load config */ void iptv_init ( void ) { pthread_t tid; + + /* Initialise URL RE */ + if (regcomp(&urlre, URL_RE, REG_ICASE | REG_EXTENDED)) { + tvherror("iptv", "failed to compile regexp"); + exit(1); + } /* Init Input */ mpegts_input_create0((mpegts_input_t*)&iptv_input, @@ -340,6 +512,8 @@ void iptv_init ( void ) &iptv_network_class, NULL, "IPTV Network", NULL); iptv_network.mn_create_mux = iptv_network_create_mux; iptv_network.mn_create_service = iptv_network_create_service; + iptv_network.mn_mux_class = iptv_network_mux_class; + iptv_network.mn_mux_create2 = iptv_network_create_mux2; /* Link */ mpegts_input_set_network((mpegts_input_t*)&iptv_input, diff --git a/src/input/mpegts/iptv/iptv_mux.c b/src/input/mpegts/iptv/iptv_mux.c index 86b288e2..df34802e 100644 --- a/src/input/mpegts/iptv/iptv_mux.c +++ b/src/input/mpegts/iptv/iptv_mux.c @@ -32,29 +32,64 @@ const idclass_t iptv_mux_class = .ic_class = "iptv_mux", .ic_caption = "IPTV Multiplex", .ic_properties = (const property_t[]){ -#if 0 - { PROPDEF1("iptv_url", "URL", - PT_STR, iptv_mux_t, mm_iptv_url) }, - { PROPDEF1("iptv_interface", "Interface", - PT_STR, iptv_mux_t, mm_iptv_interface) }, -#endif + { + .type = PT_STR, + .id = "iptv_url", + .name = "URL", + .off = offsetof(iptv_mux_t, mm_iptv_url), + }, + { + .type = PT_STR, + .id = "iptv_interface", + .name = "Interface", + .off = offsetof(iptv_mux_t, mm_iptv_interface), + }, {} } }; +static void +iptv_mux_config_save ( mpegts_mux_t *mm ) +{ + htsmsg_t *c = htsmsg_create_map(); + mpegts_mux_save(mm, c); + hts_settings_save(c, "input/iptv/muxes/%s/config", + idnode_uuid_as_str(&mm->mm_id)); + htsmsg_destroy(c); +} + +static void +iptv_mux_delete ( mpegts_mux_t *mm ) +{ + hts_settings_remove("input/iptv/muxes/%s/config", + idnode_uuid_as_str(&mm->mm_id)); + + mpegts_mux_delete(mm); +} + +static void +iptv_mux_display_name ( mpegts_mux_t *mm, char *buf, size_t len ) +{ + iptv_mux_t *im = (iptv_mux_t*)mm; + strncpy(buf, im->mm_iptv_url, len); +} + /* * Create */ iptv_mux_t * -iptv_mux_create ( const char *uuid, const char *url ) +iptv_mux_create ( const char *uuid, htsmsg_t *conf ) { /* Create Mux */ iptv_mux_t *im = - mpegts_mux_create(iptv_mux, NULL, + mpegts_mux_create(iptv_mux, uuid, (mpegts_network_t*)&iptv_network, - MPEGTS_ONID_NONE, MPEGTS_TSID_NONE, NULL); - if (url) - im->mm_iptv_url = strdup(url); + MPEGTS_ONID_NONE, MPEGTS_TSID_NONE, conf); + + /* Callbacks */ + im->mm_display_name = iptv_mux_display_name; + im->mm_config_save = iptv_mux_config_save; + im->mm_delete = iptv_mux_delete; /* Create Instance */ mpegts_mux_instance_create0(&im->mm_iptv_instance, @@ -69,50 +104,17 @@ iptv_mux_create ( const char *uuid, const char *url ) /* * Load */ -static void -iptv_mux_load_one ( iptv_mux_t *im, htsmsg_t *c ) -{ -#if 0 - const char *str; - - /* Load core */ - mpegts_mux_load_one((mpegts_mux_t*)im, c); - - /* URL */ - if ((str = htsmsg_get_str(c, "iptv_url"))) - tvh_str_update(&im->mm_iptv_url, str); -#endif -} - void iptv_mux_load_all ( void ) { - htsmsg_t *s, *m; + htsmsg_t *s, *e; htsmsg_field_t *f; - iptv_mux_t *im; - if ((s = hts_settings_load_r(1, "input/mpegts/iptv/muxes"))) { + if ((s = hts_settings_load_r(1, "input/iptv/muxes"))) { HTSMSG_FOREACH(f, s) { - if (!(m = htsmsg_get_map_by_field(f)) || !(m = htsmsg_get_map(m, "config"))) { - tvhlog(LOG_ERR, "iptv", "failed to load mux config %s", f->hmf_name); - continue; - } - printf("UUID: %s\n", f->hmf_name); - printf("CONFIG:\n"); - htsmsg_print(m); - - /* Create */ - im = iptv_mux_create(f->hmf_name, NULL); - if (!im) { - tvhlog(LOG_ERR, "iptv", "failed to load mux config %s", f->hmf_name); - continue; - } - - /* Configure */ - iptv_mux_load_one(im, m); - - /* Load services */ - iptv_service_load_all(im, f->hmf_name); + if (!(e = htsmsg_get_map_by_field(f))) continue; + if (!(e = htsmsg_get_map(e, "config"))) continue; + (void)iptv_mux_create(f->hmf_name, e); } } } diff --git a/src/input/mpegts/iptv/iptv_private.h b/src/input/mpegts/iptv/iptv_private.h index 355ff970..4635a4dd 100644 --- a/src/input/mpegts/iptv/iptv_private.h +++ b/src/input/mpegts/iptv/iptv_private.h @@ -46,9 +46,10 @@ struct iptv_mux int mm_iptv_fd; mpegts_mux_instance_t mm_iptv_instance; char *mm_iptv_url; + char *mm_iptv_interface; }; -iptv_mux_t* iptv_mux_create ( const char *uuid, const char *url ); +iptv_mux_t* iptv_mux_create ( const char *uuid, htsmsg_t *conf ); struct iptv_service {