diff --git a/src/input/mpegts/iptv/iptv.c b/src/input/mpegts/iptv/iptv.c index af40f4da..3bfad3f7 100644 --- a/src/input/mpegts/iptv/iptv.c +++ b/src/input/mpegts/iptv/iptv.c @@ -31,126 +31,56 @@ #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 +/* ************************************************************************** + * IPTV state + * *************************************************************************/ -/* - * Globals - */ iptv_input_t iptv_input; iptv_network_t iptv_network; tvhpoll_t *iptv_poll; pthread_t iptv_thread; pthread_mutex_t iptv_lock; +/* ************************************************************************** + * IPTV handlers + * *************************************************************************/ -/* - * URL processing - TODO: move to a library - */ +static RB_HEAD(,iptv_handler) iptv_handlers; -typedef struct url +static int +ih_cmp ( iptv_handler_t *a, iptv_handler_t *b ) { - char scheme[16]; - char user[128]; - char pass[128]; - char host[256]; - uint16_t port; - char path[256]; -} url_t; - -#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 ) -{ - regmatch_t m[12]; - char buf[16]; - - memset(m, 0, sizeof(m)); - - if (regexec(&urlre, str, 12, m, 0)) - return 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; + return strcasecmp(a->scheme, b->scheme); } -/* - * HTTP client - */ - -static int http_connect (url_t *url) +void +iptv_handler_register ( iptv_handler_t *ih, int num ) { - int fd, c, i; - char buf[1024]; - - if (!url->port) - url->port = strcmp("https", url->scheme) ? 80 : 443; - - /* Make connection */ - // TODO: move connection to thread - // TODO: this is really only for testing and to allow use of TVH webserver as input - tvhlog(LOG_DEBUG, "iptv", "connecting to http %s %d", - url->host, url->port); - fd = tcp_connect(url->host, url->port, buf, sizeof(buf), 10); - if (fd < 0) { - tvhlog(LOG_ERR, "iptv", "tcp_connect() failed %s", buf); - return -1; + iptv_handler_t *r; + while (num) { + r = RB_INSERT_SORTED(&iptv_handlers, ih, link, ih_cmp); + if (r) + tvhwarn("iptv", "attempt to re-register handler for %s", + ih->scheme); + num--; + ih++; } - - /* Send request (VERY basic) */ - c = snprintf(buf, sizeof(buf), "GET %s HTTP/1.1\r\n", url->path); - tvh_write(fd, buf, c); - c = snprintf(buf, sizeof(buf), "Hostname: %s\r\n", url->host); - tvh_write(fd, buf, c); - tvh_write(fd, "\r\n", 2); - - /* Read back header */ - // TODO: do this properly - i = 0; - while (1) { - if (!(c = read(fd, buf+i, 1))) - continue; - i++; - if (i == 4 && !strncmp(buf, "\r\n\r\n", 4)) - break; - memmove(buf, buf+1, 3); i = 3; - } - - return fd; } -/* - * Input definition - */ +static iptv_handler_t * +iptv_handler_find ( const char *scheme ) +{ + iptv_handler_t ih; + ih.scheme = scheme; + + return RB_FIND(&iptv_handlers, &ih, link, ih_cmp); +} + +/* ************************************************************************** + * IPTV input + * *************************************************************************/ + static const char * iptv_input_class_get_title ( idnode_t *self ) { @@ -167,170 +97,39 @@ const idclass_t iptv_input_class = { } }; -/* - * HTTP - */ -static int -iptv_input_start_http - ( iptv_mux_t *im, url_t *url, const char *name ) -{ - /* Setup connection */ - return http_connect(url); -} - -/* - * 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 = IPTV_PKT_SIZE; - 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 fd, ret = SM_CODE_TUNING_FAILED; + int ret = SM_CODE_TUNING_FAILED; iptv_mux_t *im = (iptv_mux_t*)mmi->mmi_mux; + iptv_handler_t *ih; assert(mmi == &im->mm_iptv_instance); char buf[256]; url_t url; + /* Already active */ + if (im->mm_active) + return 0; + + /* Parse 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 (!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 (fd != -1) { - tvhpoll_event_t ev; - memset(&ev, 0, sizeof(ev)); - ev.events = TVHPOLL_IN; - ev.fd = ev.data.fd = im->mm_iptv_fd = fd; - if (tvhpoll_add(iptv_poll, &ev, 1) == -1) { - tvherror("iptv", "%s - failed to add to poll q", buf); - close(fd); - } else { - im->mm_active = mmi; - im->mm_iptv_pos = 0; - im->mm_iptv_tsb = calloc(1, IPTV_PKT_SIZE); - - /* Install table handlers */ - mpegts_table_add(mmi->mmi_mux, DVB_PAT_BASE, DVB_PAT_MASK, - dvb_pat_callback, NULL, "pat", - MT_QUICKREQ| MT_CRC, DVB_PAT_PID); - - ret = 0; - } - } + /* Find scheme handler */ + ih = iptv_handler_find(url.scheme); + if (!ih) { + tvherror("iptv", "%s - unsupported scheme [%s]", buf, url.scheme); + return ret; } + + /* Start */ + pthread_mutex_lock(&iptv_lock); + im->mm_active = mmi; + im->im_handler = ih; + ret = ih->start(im, &url); pthread_mutex_unlock(&iptv_lock); return ret; @@ -341,21 +140,26 @@ iptv_input_stop_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) { iptv_mux_t *im = (iptv_mux_t*)mmi->mmi_mux; assert(mmi == &im->mm_iptv_instance); + + // Not active?? + if (!im->mm_active) + return; pthread_mutex_lock(&iptv_lock); - if (im->mm_active) { + + /* Stop */ + im->im_handler->stop(im); - // TODO: multicast will require additional work - // TODO: need to leave group? - - /* Close file */ + /* Close file */ + if (im->mm_iptv_fd > 0) { close(im->mm_iptv_fd); // removes from poll im->mm_iptv_fd = -1; - - /* Free memory */ - free(im->mm_iptv_tsb); - im->mm_iptv_tsb = NULL; } + + /* Free memory */ + free(im->mm_iptv_tsb); + im->mm_iptv_tsb = NULL; + pthread_mutex_unlock(&iptv_lock); } @@ -380,9 +184,10 @@ iptv_input_display_name ( mpegts_input_t *mi, char *buf, size_t len ) static void * iptv_input_thread ( void *aux ) { - int hlen, nfds, fd, r; + int nfds; + ssize_t len; + size_t off; iptv_mux_t *im; - mpegts_mux_t *mm; tvhpoll_event_t ev; while ( 1 ) { @@ -395,71 +200,76 @@ iptv_input_thread ( void *aux ) } else if ( nfds == 0 ) { continue; } - fd = ev.data.fd; + im = ev.data.ptr; - /* Find mux */ pthread_mutex_lock(&iptv_lock); - LIST_FOREACH(mm, &iptv_network.mn_muxes, mm_network_link) { - if (((iptv_mux_t*)mm)->mm_iptv_fd == fd) - break; - } - if (!mm) { - pthread_mutex_unlock(&iptv_lock); - continue; - } - im = (iptv_mux_t*)mm; - /* Read data */ - //tvhtrace("iptv", "read(%d)", fd); - r = read(fd, im->mm_iptv_tsb+im->mm_iptv_pos, - IPTV_PKT_SIZE-im->mm_iptv_pos); + /* No longer active */ + if (!im->mm_active) + goto done; - /* Error */ - if (r < 0) { + /* Get data */ + off = 0; + if ((len = im->im_handler->read(im, &off)) < 0) { tvhlog(LOG_ERR, "iptv", "read() error %s", strerror(errno)); - // TODO: close and remove? - continue; - } - r += im->mm_iptv_pos; - - /* RTP */ - hlen = 0; - if (!strncmp(im->mm_iptv_url, "rtp", 3)) { - if (r < 12) - goto done; - if ((im->mm_iptv_tsb[0] & 0xC0) != 0x80) - goto done; - if ((im->mm_iptv_tsb[1] & 0x7F) != 33) - goto done; - hlen = ((im->mm_iptv_tsb[0] & 0xf) * 4) + 12; - if (im->mm_iptv_tsb[0] & 0x10) { - if (r < hlen+4) - goto done; - hlen += (im->mm_iptv_tsb[hlen+2] << 8) | (im->mm_iptv_tsb[hlen+3]*4); - hlen += 4; - } - if (r < hlen || ((r - hlen) % 188) != 0) - goto done; + im->im_handler->stop(im); + goto done; } - /* TS */ -#if 0 - tvhtrace("iptv", "recv data %d (%d)", (int)r, hlen); - tvhlog_hexdump("iptv", im->mm_iptv_tsb, r); -#endif - im->mm_iptv_pos = mpegts_input_recv_packets((mpegts_input_t*)&iptv_input, - &im->mm_iptv_instance, - im->mm_iptv_tsb+hlen, r-hlen, NULL, NULL, "iptv"); - //tvhtrace("iptv", "pos = %d", im->mm_iptv_pos); + /* Pass on */ + im->mm_iptv_pos + = mpegts_input_recv_packets((mpegts_input_t*)&iptv_input, + im->mm_active, + im->mm_iptv_tsb+off, len, + NULL, NULL, "iptv"); + done: pthread_mutex_unlock(&iptv_lock); } return NULL; } -/* - * Network definition - */ +void +iptv_input_mux_started ( iptv_mux_t *im ) +{ + tvhpoll_event_t ev = { 0 }; + char buf[256]; + im->mm_display_name((mpegts_mux_t*)im, buf, sizeof(buf)); + + /* Allocate input buffer */ + im->mm_iptv_pos = 0; + im->mm_iptv_tsb = calloc(1, IPTV_PKT_SIZE); + + /* Setup poll */ + if (im->mm_iptv_fd > 0) { + ev.fd = im->mm_iptv_fd; + ev.events = TVHPOLL_IN; + ev.data.ptr = im; + + /* Error? */ + if (tvhpoll_add(iptv_poll, &ev, 1) == -1) { + tvherror("iptv", "%s - failed to add to poll q", buf); + close(im->mm_iptv_fd); + im->mm_iptv_fd = -1; + return; + } + } + + /* Install table handlers */ + // Note: we don't install NIT as we can't do mux discovery + // TODO: not currently installing ATSC handler + mpegts_table_add((mpegts_mux_t*)im, DVB_SDT_BASE, DVB_SDT_MASK, + dvb_sdt_callback, NULL, "sdt", + MT_QUICKREQ | MT_CRC, DVB_SDT_PID); + mpegts_table_add((mpegts_mux_t*)im, DVB_PAT_BASE, DVB_PAT_MASK, + dvb_pat_callback, NULL, "pat", + MT_QUICKREQ | MT_CRC, DVB_PAT_PID); +} + +/* ************************************************************************** + * IPTV network + * *************************************************************************/ + extern const idclass_t mpegts_network_class; const idclass_t iptv_network_class = { .ic_super = &mpegts_network_class, @@ -492,18 +302,16 @@ iptv_network_mux_class ( mpegts_network_t *mm ) return &iptv_mux_class; } -/* - * Intialise and load config - */ +/* ************************************************************************** + * IPTV initialise + * *************************************************************************/ + 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); - } + + /* Register handlers */ + iptv_http_init(); /* Init Input */ mpegts_input_create0((mpegts_input_t*)&iptv_input, @@ -529,7 +337,6 @@ void iptv_init ( void ) tvhthread_create(&tid, NULL, mpegts_input_table_thread, &iptv_input, 1); /* Setup TS thread */ - // TODO: could set this up only when needed iptv_poll = tvhpoll_create(10); pthread_mutex_init(&iptv_lock, NULL); tvhthread_create(&iptv_thread, NULL, iptv_input_thread, NULL, 1); @@ -538,7 +345,6 @@ void iptv_init ( void ) iptv_mux_load_all(); } - /****************************************************************************** * Editor Configuration * diff --git a/src/input/mpegts/iptv/iptv_http.c b/src/input/mpegts/iptv/iptv_http.c new file mode 100644 index 00000000..91ef3860 --- /dev/null +++ b/src/input/mpegts/iptv/iptv_http.c @@ -0,0 +1,122 @@ +/* + * IPTV - HTTP/HTTPS handler + * + * Copyright (C) 2013 Adam Sutton + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "tvheadend.h" +#include "iptv_private.h" +#include "http.h" + +#if ENABLE_CURL + +/* + * Connected + */ +static void +iptv_http_conn ( void *p ) +{ + pthread_mutex_lock(&global_lock); + iptv_input_mux_started(p); + pthread_mutex_unlock(&global_lock); +} + +/* + * Receive data + */ +static size_t +iptv_http_data + ( void *p, void *buf, size_t len ) +{ + uint8_t *tsb; + size_t ret = len; + iptv_mux_t *im = p; + + pthread_mutex_lock(&iptv_lock); + + tsb = im->mm_iptv_tsb + im->mm_iptv_pos; + len = MIN(len, IPTV_PKT_SIZE - im->mm_iptv_pos); + + memcpy(tsb, buf, len); + + im->mm_iptv_pos + = mpegts_input_recv_packets((mpegts_input_t*)&iptv_input, + im->mm_active, + im->mm_iptv_tsb, + im->mm_iptv_pos + len, + NULL, NULL, "iptv"); + + pthread_mutex_unlock(&iptv_lock); + + return ret; +} + +/* + * Setup HTTP(S) connection + */ +static int +iptv_http_start + ( iptv_mux_t *im, const url_t *u ) +{ + http_client_t *hc; + if (!(hc = http_connect(u, iptv_http_conn, iptv_http_data, NULL, im))) + return SM_CODE_TUNING_FAILED; + im->im_data = hc; + + return 0; +} + +/* + * Stop connection + */ +static void +iptv_http_stop + ( iptv_mux_t *im ) +{ + http_close(im->im_data); +} + + +/* + * Initialise HTTP handler + */ + +void +iptv_http_init ( void ) +{ + static iptv_handler_t ih[] = { + { + .scheme = "http", + .start = iptv_http_start, + .stop = iptv_http_stop, + }, + { + .scheme = "https", + .start = iptv_http_start, + .stop = iptv_http_stop, + } + }; + iptv_handler_register(ih, 2); +} + +#else /* ENABLE_CURL */ + +void +iptv_http_init ( void ) +{ +} + +#endif /* ENABLE_CURL */ diff --git a/src/input/mpegts/iptv/iptv_private.h b/src/input/mpegts/iptv/iptv_private.h index 8f6521e8..0f459146 100644 --- a/src/input/mpegts/iptv/iptv_private.h +++ b/src/input/mpegts/iptv/iptv_private.h @@ -23,19 +23,41 @@ #include "input/mpegts.h" #include "input/mpegts/iptv.h" #include "htsbuf.h" +#include "url.h" #define IPTV_PKT_SIZE (300*188) +extern pthread_mutex_t iptv_lock; + typedef struct iptv_input iptv_input_t; typedef struct iptv_network iptv_network_t; typedef struct iptv_mux iptv_mux_t; typedef struct iptv_service iptv_service_t; +typedef struct iptv_handler iptv_handler_t; + +struct iptv_handler +{ + const char *scheme; + int (*start) ( iptv_mux_t *im, const url_t *url ); + void (*stop) ( iptv_mux_t *im ); + + // Return number of available bytes, with optional offset + // from start of mux buffer (useful for things with wrapper + // around TS) + ssize_t (*read) ( iptv_mux_t *im, size_t *off ); + + RB_ENTRY(iptv_handler) link; +}; + +void iptv_handler_register ( iptv_handler_t *ih, int num ); struct iptv_input { mpegts_input_t; }; +void iptv_input_mux_started ( iptv_mux_t *im ); + struct iptv_network { mpegts_network_t; @@ -52,6 +74,10 @@ struct iptv_mux uint8_t *mm_iptv_tsb; int mm_iptv_pos; + + iptv_handler_t *im_handler; + + void *im_data; }; iptv_mux_t* iptv_mux_create ( const char *uuid, htsmsg_t *conf ); @@ -68,7 +94,10 @@ iptv_service_t *iptv_service_create0 extern iptv_input_t iptv_input; extern iptv_network_t iptv_network; -void iptv_mux_load_all ( void ); +void iptv_mux_load_all ( void ); + +void iptv_http_init ( void ); +void iptv_udp_init ( void ); #endif /* __IPTV_PRIVATE_H__ */ diff --git a/src/input/mpegts/iptv/iptv_udp.c b/src/input/mpegts/iptv/iptv_udp.c new file mode 100644 index 00000000..a490edd4 --- /dev/null +++ b/src/input/mpegts/iptv/iptv_udp.c @@ -0,0 +1,243 @@ +/* + * IPTV - UDP/RTP handler + * + * Copyright (C) 2013 Adam Sutton + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "tvheadend.h" +#include "iptv_private.h" + +#include +#include +#include +#include +#include +#include +#include +#if defined(PLATFORM_LINUX) +#include +#elif defined(PLATFORM_FREEBSD) +# include +# include +# ifndef IPV6_ADD_MEMBERSHIP +# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +# endif +#endif + + +/* + * Connect UDP/RTP + */ +static int +iptv_udp_start ( iptv_mux_t *im, const url_t *url ) +{ + int fd, solip, rxsize, reuse = 1, ipv6 = 0; + struct ifreq ifr; + struct in_addr saddr; + struct in6_addr s6addr; + char name[256], buf[256]; + + im->mm_display_name((mpegts_mux_t*)im, buf, sizeof(buf)); + + /* Determine if this is IPv6 */ + if (!inet_pton(AF_INET, url->host, &saddr)) { + ipv6 = 1; + if (!inet_pton(AF_INET6, url->host, &s6addr)) { + tvherror("iptv", "%s - failed to process host", name); + return SM_CODE_TUNING_FAILED; + } + } + + /* Open socket */ + if ((fd = tvh_socket(ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, 0)) == -1) { + tvherror("iptv", "%s - failed to create socket [%s]", + name, strerror(errno)); + return SM_CODE_TUNING_FAILED; + } + + /* Mark reuse address */ + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + /* Bind to interface */ + memset(&ifr, 0, sizeof(ifr)); + if (im->mm_iptv_interface && *im->mm_iptv_interface) { + 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; + } + } + + /* IPv4 */ + if (!ipv6) { + struct ip_mreqn m; + struct sockaddr_in sin; + memset(&m, 0, sizeof(m)); + memset(&sin, 0, sizeof(sin)); + + /* Bind */ + sin.sin_family = AF_INET; + sin.sin_port = htons(url->port); + sin.sin_addr = saddr; + if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + inet_ntop(AF_INET, &sin.sin_addr, buf, sizeof(buf)); + tvherror("iptv", "%s - cannot bind %s:%hd [e=%s]", + name, buf, ntohs(sin.sin_port), strerror(errno)); + goto error; + } + + /* Join group */ + m.imr_multiaddr = sin.sin_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(m))) { + inet_ntop(AF_INET, &m.imr_multiaddr, buf, sizeof(buf)); + tvherror("iptv", "%s - cannot join %s [%s]", + name, buf, strerror(errno)); + goto error; + } + + /* Bind to IPv6 group */ + } else { + struct ipv6_mreq m; + struct sockaddr_in6 sin; + memset(&m, 0, sizeof(m)); + memset(&sin, 0, sizeof(sin)); + + /* Bind */ + sin.sin6_family = AF_INET6; + sin.sin6_port = htons(url->port); + sin.sin6_addr = s6addr; + if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + inet_ntop(AF_INET6, &sin.sin6_addr, buf, sizeof(buf)); + tvherror("iptv", "%s - cannot bind %s:%hd [e=%s]", + name, buf, ntohs(sin.sin6_port), strerror(errno)); + goto error; + } + + /* Join group */ + m.ipv6mr_multiaddr = sin.sin6_addr; +#if defined(PLATFORM_LINUX) + m.ipv6mr_interface = ifr.ifr_ifindex; +#elif defined(PLATFORM_FREEBSD) + m.ipv6mr_interface = ifr.ifr_index; +#endif +#ifdef SOL_IPV6 + if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m, sizeof(m))) { + inet_ntop(AF_INET, &m.ipv6mr_multiaddr, buf, sizeof(buf)); + tvherror("iptv", "%s - cannot join %s [%s]", + name, buf, strerror(errno)); + goto error; + } +#else + tvherror("iptv", "IPv6 multicast not supported"); + goto error; +#endif + } + + /* Increase RX buffer size */ + rxsize = IPTV_PKT_SIZE; + 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 ssize_t +iptv_udp_read ( iptv_mux_t *im, size_t *off ) +{ + /* UDP/RTP should not have TS packets straddling datagrams, I think! */ + im->mm_iptv_pos = 0; + + /* Read */ + return read(im->mm_iptv_fd, im->mm_iptv_tsb, IPTV_PKT_SIZE); +} + +static ssize_t +iptv_rtp_read ( iptv_mux_t *im, size_t *off ) +{ + ssize_t len, hlen; + + /* Raw packet */ + len = iptv_udp_read(im, NULL); + if (len < 0) + return -1; + + /* Strip RTP header */ + if (len < 12) + return 0; // ignore + if ((im->mm_iptv_tsb[0] & 0xC0) != 0x80) + return 0; + if ((im->mm_iptv_tsb[1] & 0x7F) != 33) + return 0; + hlen = ((im->mm_iptv_tsb[0] & 0xf) * 4) + 12; + if (im->mm_iptv_tsb[0] & 0x10) { + if (len < hlen+4) + return 0; + hlen += (im->mm_iptv_tsb[hlen+2] << 8) | (im->mm_iptv_tsb[hlen+3]*4); + hlen += 4; + } + if (len < hlen || ((len - hlen) % 188) != 0) + return 0; + + /* OK */ + *off = hlen; + return len-hlen; +} + +/* + * Initialise UDP handler + */ + +void +iptv_udp_init ( void ) +{ + static iptv_handler_t ih[] = { + { + .scheme = "udp", + .start = iptv_udp_start, + .read = iptv_udp_read, + }, + { + .scheme = "rtp", + .start = iptv_udp_start, + .read = iptv_rtp_read, + } + }; + iptv_handler_register(ih, 2); +} diff --git a/src/main.c b/src/main.c index ddc31cb3..07dd459d 100644 --- a/src/main.c +++ b/src/main.c @@ -732,6 +732,7 @@ main(int argc, char **argv) timeshift_init(); #endif + http_client_init(); tcp_server_init(opt_ipv6); http_server_init(opt_bindaddr); webui_init();