diff -rupN tvheadend-3.4patch1/src/iptv_input.c tvheadend-3.4patch2/src/iptv_input.c --- tvheadend-3.4patch1/src/iptv_input.c 2013-07-11 21:17:34.000000000 +0200 +++ tvheadend-3.4patch2/src/iptv_input.c 2014-04-04 16:00:04.000000000 +0200 @@ -40,6 +40,7 @@ #include "tsdemux.h" #include "psi.h" #include "settings.h" +#include "tcp.h" static int iptv_thread_running; static int iptv_epollfd; @@ -48,6 +49,57 @@ static pthread_mutex_t iptv_recvmutex; struct service_list iptv_all_services; /* All IPTV services */ static struct service_list iptv_active_services; /* Currently enabled */ + +/* + * URL processing - TODO: move to a library + */ +typedef struct +url +{ + char buf[2048]; + const char *scheme; + const char *host; + uint16_t port; + const char *path; +} url_t; + +static int +url_parse ( url_t *up, const char *urlstr ) +{ + char *t1, *t2; + strcpy(up->buf, urlstr); + + /* Scheme */ + up->scheme = t1 = up->buf; + if (!(t2 = strstr(t1, "://"))) { + 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); + } + + return 0; +} + + /** * PAT parser. We only parse a single program. CRC has already been verified */ @@ -101,7 +153,6 @@ iptv_ts_input(service_t *t, const uint8_ uint16_t pid = ((tsb[1] & 0x1f) << 8) | tsb[2]; if(pid == 0) { - if(t->s_pat_section == NULL) t->s_pat_section = calloc(1, sizeof(psi_section_t)); psi_section_reassemble(t->s_pat_section, tsb, 1, iptv_got_pat, t); @@ -124,16 +175,15 @@ iptv_ts_input(service_t *t, const uint8_ static void * iptv_thread(void *aux) { - int nfds, fd, r, j, hlen; - uint8_t tsb[65536], *buf; + int nfds, fd, type, tlen = sizeof(int), r, j, hlen, err = 0; + uint8_t *tsb, *buf, real_buf[65536]; struct epoll_event ev; service_t *t; while(1) { nfds = epoll_wait(iptv_epollfd, &ev, 1, -1); if(nfds == -1) { - tvhlog(LOG_ERR, "IPTV", "epoll() error -- %s, sleeping 1 second", - strerror(errno)); + tvhlog(LOG_ERR, "IPTV", "epoll() error -- %s, sleeping 1 second", strerror(errno)); sleep(1); continue; } @@ -142,51 +192,77 @@ iptv_thread(void *aux) continue; fd = ev.data.fd; - r = read(fd, tsb, sizeof(tsb)); + tsb = real_buf + 188; + r = read(fd, tsb, sizeof(real_buf) - 188); - if(r > 1 && tsb[0] == 0x47 && (r % 188) == 0) { + getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, (socklen_t *)&tlen); + + if(type == SOCK_STREAM || (r > 1 && tsb[0] == 0x47 && (r % 188) == 0)) { /* Looks like raw TS in UDP */ buf = tsb; } else { /* Check for valid RTP packets */ if(r < 12) - continue; + continue; if((tsb[0] & 0xc0) != 0x80) - continue; + continue; if((tsb[1] & 0x7f) != 33) - continue; - + continue; + hlen = (tsb[0] & 0xf) * 4 + 12; if(tsb[0] & 0x10) { - // Extension (X bit) == true + // Extension (X bit) == true - if(r < hlen + 4) - continue; // Packet size < hlen + extension header + if(r < hlen + 4) + continue; // Packet size < hlen + extension header - // Skip over extension header (last 2 bytes of header is length) - hlen += ((tsb[hlen + 2] << 8) | tsb[hlen + 3]) * 4; - // Add the extension header itself (EHL does not inc header) - hlen += 4; + // Skip over extension header (last 2 bytes of header is length) + hlen += ((tsb[hlen + 2] << 8) | tsb[hlen + 3]) * 4; + // Add the extension header itself (EHL does not inc header) + hlen += 4; } if(r < hlen || (r - hlen) % 188 != 0) - continue; + continue; buf = tsb + hlen; r -= hlen; } pthread_mutex_lock(&iptv_recvmutex); - + LIST_FOREACH(t, &iptv_active_services, s_active_link) { if(t->s_iptv_fd != fd) - continue; - - for(j = 0; j < r; j += 188) - iptv_ts_input(t, buf + j); + continue; + + if(t->s_iptv_url != NULL) { + buf = tsb - t->s_iptv_tsb_len; + r += t->s_iptv_tsb_len; + memcpy(buf, t->s_iptv_tsb, t->s_iptv_tsb_len); + + // Attempt to re-sync a TS stream (3 valid sync's in a row) + if(buf[0] != 0x47) { + err = 1; + while (err && (r > 376)) { + buf++; r--; + err = (buf[0] != 0x47) || (buf[188] != 0x47) || (buf[376] != 0x47); + } + } + + for(j = 0; j <= r - 188; j += 188) { + iptv_ts_input(t, buf + j); + } + + t->s_iptv_tsb_len = r % 188; + memcpy(t->s_iptv_tsb, buf+((r/188)*188), t->s_iptv_tsb_len); + } + else{ + for(j = 0; j <= r - 188; j += 188) + iptv_ts_input(t, buf + j); + } } pthread_mutex_unlock(&iptv_recvmutex); } @@ -201,8 +277,10 @@ static int iptv_service_start(service_t *t, unsigned int weight, int force_start) { pthread_t tid; - int fd; + int fd, c, i, timeout; char straddr[INET6_ADDRSTRLEN]; + char buf[1024]; + url_t url; struct ip_mreqn m; struct ipv6_mreq m6; struct sockaddr_in sin; @@ -218,101 +296,129 @@ iptv_service_start(service_t *t, unsigne pthread_create(&tid, NULL, iptv_thread, NULL); } - /* Now, open the real socket for UDP */ - if(t->s_iptv_group.s_addr!=0) { - fd = tvh_socket(AF_INET, SOCK_DGRAM, 0); - - } - else { - fd = tvh_socket(AF_INET6, SOCK_DGRAM, 0); - } - if(fd == -1) { - tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot open socket", t->s_identifier); - return -1; - } - - /* First, resolve interface name */ - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, IFNAMSIZ, "%s", t->s_iptv_iface); - ifr.ifr_name[IFNAMSIZ - 1] = 0; - if(ioctl(fd, SIOCGIFINDEX, &ifr)) { - tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s", - t->s_identifier, t->s_iptv_iface); - close(fd); - return -1; - } + if(t->s_iptv_url != NULL){ + if(url_parse(&url, t->s_iptv_url)) + return -1; - /* Bind to IPv4 multicast group */ - if(t->s_iptv_group.s_addr!=0) { - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(t->s_iptv_port); - sin.sin_addr.s_addr = t->s_iptv_group.s_addr; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &m, sizeof(struct ip_mreqn)); - if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { - tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot bind %s:%d -- %s", - t->s_identifier, inet_ntoa(sin.sin_addr), t->s_iptv_port, - strerror(errno)); - close(fd); + /* 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; + } + + /* 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; + timeout = 2000; + while (1) { + if (!(c = read(fd, buf+i, 1))) { + usleep(100000); + timeout-=100; + if (timeout <= 0) { + tvhlog(LOG_ERR, "IPTV", "Timeout waiting for HTTP header"); + return -1; + } + continue; + } + i++; + if (i == 4 && !strncmp(buf, "\r\n\r\n", 4)) + break; + memmove(buf, buf+1, 3); i = 3; } - /* Join IPv4 group */ - memset(&m, 0, sizeof(m)); - m.imr_multiaddr.s_addr = t->s_iptv_group.s_addr; - m.imr_address.s_addr = 0; - m.imr_ifindex = ifr.ifr_ifindex; - - if(setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &m, - sizeof(struct ip_mreqn)) == -1) { - tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s", - t->s_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno)); - close(fd); - return -1; + } + else { + /* Now, open the real socket for UDP */ + if(t->s_iptv_group.s_addr!=0) { + fd = tvh_socket(AF_INET, SOCK_DGRAM, 0); } - } else { - /* Bind to IPv6 multicast group */ - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = htons(t->s_iptv_port); - sin6.sin6_addr = t->s_iptv_group6; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &m6, sizeof(struct ipv6_mreq)); - if(bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) { - inet_ntop(AF_INET6, &sin6.sin6_addr, straddr, sizeof(straddr)); - tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot bind %s:%d -- %s", - t->s_identifier, straddr, t->s_iptv_port, - strerror(errno)); - close(fd); + else { + fd = tvh_socket(AF_INET6, SOCK_DGRAM, 0); + } + if(fd == -1) { + tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot open socket", t->s_identifier); return -1; } - /* Join IPv6 group */ - memset(&m6, 0, sizeof(m6)); - m6.ipv6mr_multiaddr = t->s_iptv_group6; - m6.ipv6mr_interface = ifr.ifr_ifindex; - - if(setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m6, - sizeof(struct ipv6_mreq)) == -1) { - inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr, - straddr, sizeof(straddr)); - tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s", - t->s_identifier, straddr, strerror(errno)); + + /* First, resolve interface name */ + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", t->s_iptv_iface); + ifr.ifr_name[IFNAMSIZ - 1] = 0; + if(ioctl(fd, SIOCGIFINDEX, &ifr)) { + tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s", t->s_identifier, t->s_iptv_iface); close(fd); return -1; } - } + /* Bind to IPv4 multicast group */ + if(t->s_iptv_group.s_addr!=0) { + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(t->s_iptv_port); + sin.sin_addr.s_addr = t->s_iptv_group.s_addr; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &m, sizeof(struct ip_mreqn)); + if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot bind %s:%d -- %s", t->s_identifier, inet_ntoa(sin.sin_addr), t->s_iptv_port, strerror(errno)); + close(fd); + return -1; + } + /* Join IPv4 group */ + memset(&m, 0, sizeof(m)); + m.imr_multiaddr.s_addr = t->s_iptv_group.s_addr; + m.imr_address.s_addr = 0; + m.imr_ifindex = ifr.ifr_ifindex; + + if(setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &m, sizeof(struct ip_mreqn)) == -1) { + tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s", t->s_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno)); + close(fd); + return -1; + } + } else { + /* Bind to IPv6 multicast group */ + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(t->s_iptv_port); + sin6.sin6_addr = t->s_iptv_group6; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &m6, sizeof(struct ipv6_mreq)); + if(bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) { + inet_ntop(AF_INET6, &sin6.sin6_addr, straddr, sizeof(straddr)); + tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot bind %s:%d -- %s", t->s_identifier, straddr, t->s_iptv_port, strerror(errno)); + close(fd); + return -1; + } + /* Join IPv6 group */ + memset(&m6, 0, sizeof(m6)); + m6.ipv6mr_multiaddr = t->s_iptv_group6; + m6.ipv6mr_interface = ifr.ifr_ifindex; + + if(setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m6, sizeof(struct ipv6_mreq)) == -1) { + inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr, straddr, sizeof(straddr)); + tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s", t->s_identifier, straddr, strerror(errno)); + close(fd); + return -1; + } + } - int resize = 262142; - if(setsockopt(fd,SOL_SOCKET,SO_RCVBUF, &resize, sizeof(resize)) == -1) - tvhlog(LOG_WARNING, "IPTV", - "Can not icrease UDP receive buffer size to %d -- %s", - resize, strerror(errno)); + int resize = 262142; + if(setsockopt(fd,SOL_SOCKET,SO_RCVBUF, &resize, sizeof(resize)) == -1) + tvhlog(LOG_WARNING, "IPTV", "Can not icrease UDP receive buffer size to %d -- %s", resize, strerror(errno)); + } memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; ev.data.fd = fd; if(epoll_ctl(iptv_epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { - tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot add to epoll set -- %s", - t->s_identifier, strerror(errno)); + tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot add to epoll set -- %s", t->s_identifier, strerror(errno)); close(fd); return -1; } @@ -350,49 +456,41 @@ iptv_service_stop(service_t *t) assert(t->s_iptv_fd >= 0); - /* First, resolve interface name */ - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, IFNAMSIZ, "%s", t->s_iptv_iface); - ifr.ifr_name[IFNAMSIZ - 1] = 0; - if(ioctl(t->s_iptv_fd, SIOCGIFINDEX, &ifr)) { - tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s", - t->s_identifier, t->s_iptv_iface); - } - - if(t->s_iptv_group.s_addr != 0) { - - struct ip_mreqn m; - memset(&m, 0, sizeof(m)); - /* Leave multicast group */ - m.imr_multiaddr.s_addr = t->s_iptv_group.s_addr; - m.imr_address.s_addr = 0; - m.imr_ifindex = ifr.ifr_ifindex; - - if(setsockopt(t->s_iptv_fd, SOL_IP, IP_DROP_MEMBERSHIP, &m, - sizeof(struct ip_mreqn)) == -1) { - tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s", - t->s_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno)); - } - } else { - char straddr[INET6_ADDRSTRLEN]; + if(t->s_iptv_url == NULL) { + /* First, resolve interface name */ + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", t->s_iptv_iface); + ifr.ifr_name[IFNAMSIZ - 1] = 0; + if(ioctl(t->s_iptv_fd, SIOCGIFINDEX, &ifr)) { + tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s", t->s_identifier, t->s_iptv_iface); + } + + if(t->s_iptv_group.s_addr != 0) { + struct ip_mreqn m; + memset(&m, 0, sizeof(m)); + /* Leave multicast group */ + m.imr_multiaddr.s_addr = t->s_iptv_group.s_addr; + m.imr_address.s_addr = 0; + m.imr_ifindex = ifr.ifr_ifindex; - struct ipv6_mreq m6; - memset(&m6, 0, sizeof(m6)); - - m6.ipv6mr_multiaddr = t->s_iptv_group6; - m6.ipv6mr_interface = ifr.ifr_ifindex; - - if(setsockopt(t->s_iptv_fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &m6, - sizeof(struct ipv6_mreq)) == -1) { - inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr, - straddr, sizeof(straddr)); + if(setsockopt(t->s_iptv_fd, SOL_IP, IP_DROP_MEMBERSHIP, &m, sizeof(struct ip_mreqn)) == -1) { + tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s", t->s_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno)); + } + } else { + char straddr[INET6_ADDRSTRLEN]; - tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s", - t->s_identifier, straddr, strerror(errno)); - } + struct ipv6_mreq m6; + memset(&m6, 0, sizeof(m6)); + m6.ipv6mr_multiaddr = t->s_iptv_group6; + m6.ipv6mr_interface = ifr.ifr_ifindex; + if(setsockopt(t->s_iptv_fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &m6, sizeof(struct ipv6_mreq)) == -1) { + inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr, straddr, sizeof(straddr)); + tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s", t->s_identifier, straddr, strerror(errno)); + } + } } close(t->s_iptv_fd); // Automatically removes fd from epoll set @@ -423,9 +521,14 @@ iptv_service_save(service_t *t) if(t->s_iptv_iface) htsmsg_add_str(m, "interface", t->s_iptv_iface); - if(t->s_iptv_group.s_addr!= 0) { - inet_ntop(AF_INET, &t->s_iptv_group, abuf, sizeof(abuf)); - htsmsg_add_str(m, "group", abuf); + if(t->s_iptv_url != 0){ + htsmsg_add_str(m, "url", t->s_iptv_url); + } + else{ + if(t->s_iptv_group.s_addr!= 0) { + inet_ntop(AF_INET, &t->s_iptv_group, abuf, sizeof(abuf)); + htsmsg_add_str(m, "group", abuf); + } } if(IN6_IS_ADDR_MULTICAST(t->s_iptv_group6.s6_addr) ) { inet_ntop(AF_INET6, &t->s_iptv_group6, abuf6, sizeof(abuf6)); @@ -435,13 +538,13 @@ iptv_service_save(service_t *t) htsmsg_add_str(m, "channelname", t->s_ch->ch_name); htsmsg_add_u32(m, "mapped", 1); } - + pthread_mutex_lock(&t->s_stream_mutex); + service_make_nicename(t); psi_save_service_settings(m, t); pthread_mutex_unlock(&t->s_stream_mutex); - - hts_settings_save(m, "iptvservices/%s", - t->s_identifier); + + hts_settings_save(m, "iptvservices/%s", t->s_identifier); htsmsg_destroy(m); } @@ -453,9 +556,7 @@ iptv_service_save(service_t *t) static int iptv_service_quality(service_t *t) { - if(t->s_iptv_iface == NULL || - (t->s_iptv_group.s_addr == 0 && t->s_iptv_group6.s6_addr == 0) || - t->s_iptv_port == 0) + if(t->s_iptv_url == NULL && (t->s_iptv_iface == NULL || (t->s_iptv_group.s_addr == 0 && t->s_iptv_group6.s6_addr == 0) || t->s_iptv_port == 0)) return 0; return 100; @@ -481,12 +582,17 @@ iptv_service_setsourceinfo(service_t *t, si->si_type = S_MPEG_TS; si->si_adapter = t->s_iptv_iface ? strdup(t->s_iptv_iface) : NULL; - if(t->s_iptv_group.s_addr != 0) { - si->si_mux = strdup(inet_ntoa(t->s_iptv_group)); + if(t->s_iptv_url != 0){ + si->si_mux = strdup(t->s_iptv_url); } - else { - inet_ntop(AF_INET6, &t->s_iptv_group6, straddr, sizeof(straddr)); - si->si_mux = strdup(straddr); + else{ + if(t->s_iptv_group.s_addr != 0) { + si->si_mux = strdup(inet_ntoa(t->s_iptv_group)); + } + else { + inet_ntop(AF_INET6, &t->s_iptv_group6, straddr, sizeof(straddr)); + si->si_mux = strdup(straddr); + } } } @@ -528,7 +634,7 @@ iptv_service_find(const char *id, int cr LIST_FOREACH(t, &iptv_all_services, s_group_link) if(!strcmp(t->s_identifier, id)) - return t; + return t; } if(create == 0) @@ -588,25 +694,30 @@ iptv_service_load(void) else old = 1; } - + HTSMSG_FOREACH(f, l) { if((c = htsmsg_get_map_by_field(f)) == NULL) continue; if(htsmsg_get_u32(c, "pmt", &pmt)) continue; - + t = iptv_service_find(f->hmf_name, 1); t->s_pmt_pid = pmt; tvh_str_update(&t->s_iptv_iface, htsmsg_get_str(c, "interface")); - if((s = htsmsg_get_str(c, "group")) != NULL){ - if (!inet_pton(AF_INET, s, &t->s_iptv_group.s_addr)) { - inet_pton(AF_INET6, s, &t->s_iptv_group6.s6_addr); + if((s = htsmsg_get_str(c, "url")) != NULL){ + tvh_str_update(&t->s_iptv_url, htsmsg_get_str(c, "url")); + } + else{ + if((s = htsmsg_get_str(c, "group")) != NULL){ + if (!inet_pton(AF_INET, s, &t->s_iptv_group.s_addr)) { + inet_pton(AF_INET6, s, &t->s_iptv_group6.s6_addr); + } } } - + if(!htsmsg_get_u32(c, "port", &u32)) t->s_iptv_port = u32; @@ -622,11 +733,11 @@ iptv_service_load(void) service_make_nicename(t); psi_load_service_settings(c, t); pthread_mutex_unlock(&t->s_stream_mutex); - + s = htsmsg_get_str(c, "channelname"); if(htsmsg_get_u32(c, "mapped", &u32)) u32 = 0; - + if(s && u32) service_map_channel(t, channel_find_by_name(s, 1, 0), 0); diff -rupN tvheadend-3.4patch1/src/service.h tvheadend-3.4patch2/src/service.h --- tvheadend-3.4patch1/src/service.h 2013-07-11 21:17:34.000000000 +0200 +++ tvheadend-3.4patch2/src/service.h 2013-10-04 08:00:04.000000000 +0200 @@ -377,10 +377,13 @@ typedef struct service { * IPTV members */ char *s_iptv_iface; + char *s_iptv_url; struct in_addr s_iptv_group; struct in6_addr s_iptv_group6; uint16_t s_iptv_port; int s_iptv_fd; + uint8_t s_iptv_tsb[188]; + int s_iptv_tsb_len; /** * For per-transport PAT/PMT parsers, allocated on demand diff -rupN tvheadend-3.4patch1/src/version.c tvheadend-3.4patch2/src/version.c --- tvheadend-3.4patch1/src/version.c 1970-01-01 01:00:00.000000000 +0100 +++ tvheadend-3.4patch2/src/version.c 2013-10-02 08:00:02.000000000 +0200 @@ -0,0 +1 @@ +const char *tvheadend_version = ""; diff -rupN tvheadend-3.4patch1/src/webui/extjs.c tvheadend-3.4patch2/src/webui/extjs.c --- tvheadend-3.4patch1/src/webui/extjs.c 2013-07-11 21:17:34.000000000 +0200 +++ tvheadend-3.4patch2/src/webui/extjs.c 2014-04-24 16:00:04.000000000 +0200 @@ -1745,12 +1745,18 @@ service_update_iptv(htsmsg_t *in) } if((s = htsmsg_get_str(c, "group")) != NULL) { - if(!inet_pton(AF_INET, s, &t->s_iptv_group.s_addr)){ - inet_pton(AF_INET6, s, &t->s_iptv_group6.s6_addr); + if(!strncmp(s, "http", 4)){ + tvh_str_update(&t->s_iptv_url, s); + } + else { + t->s_iptv_url = NULL; + + if(!inet_pton(AF_INET, s, &t->s_iptv_group.s_addr)) { + inet_pton(AF_INET6, s, &t->s_iptv_group6.s6_addr); + } } save = 1; } - save |= tvh_str_update(&t->s_iptv_iface, htsmsg_get_str(c, "interface")); if(save) @@ -1772,13 +1778,18 @@ build_record_iptv(service_t *t) htsmsg_add_str(r, "channelname", t->s_ch ? t->s_ch->ch_name : ""); htsmsg_add_str(r, "interface", t->s_iptv_iface ?: ""); - if(t->s_iptv_group.s_addr != 0){ - inet_ntop(AF_INET, &t->s_iptv_group, abuf, sizeof(abuf)); - htsmsg_add_str(r, "group", t->s_iptv_group.s_addr ? abuf : ""); + if(t->s_iptv_url != 0) { + htsmsg_add_str(r, "group", t->s_iptv_url ?: ""); } else { - inet_ntop(AF_INET6, &t->s_iptv_group6, abuf6, sizeof(abuf6)); - htsmsg_add_str(r, "group", t->s_iptv_group6.s6_addr ? abuf6 : ""); + if(t->s_iptv_group.s_addr != 0) { + inet_ntop(AF_INET, &t->s_iptv_group, abuf, sizeof(abuf)); + htsmsg_add_str(r, "group", t->s_iptv_group.s_addr ? abuf : ""); + } + else { + inet_ntop(AF_INET6, &t->s_iptv_group6, abuf6, sizeof(abuf6)); + htsmsg_add_str(r, "group", t->s_iptv_group6.s6_addr ? abuf6 : ""); + } } htsmsg_add_u32(r, "port", t->s_iptv_port); @@ -1796,7 +1807,10 @@ iptv_servicecmp(const void *A, const voi service_t *a = *(service_t **)A; service_t *b = *(service_t **)B; - return memcmp(&a->s_iptv_group, &b->s_iptv_group, 4); + if(a->s_iptv_url != 0 && b->s_iptv_url != 0) + return strcmp(a->s_iptv_url, b->s_iptv_url); + else + return memcmp(&a->s_iptv_group, &b->s_iptv_group, 4); } /** diff -rupN tvheadend-3.4patch1/src/webui/static/app/iptv.js tvheadend-3.4patch2/src/webui/static/app/iptv.js --- tvheadend-3.4patch1/src/webui/static/app/iptv.js 2013-07-11 21:17:34.000000000 +0200 +++ tvheadend-3.4patch2/src/webui/static/app/iptv.js 2013-10-04 08:00:04.000000000 +0200 @@ -48,7 +48,7 @@ tvheadend.iptv = function(adapterId) { { header : "Channel name", dataIndex : 'channelname', - width : 150, + width : 100, renderer : function(value, metadata, record, row, col, store) { return value ? value : 'Unmapped'; @@ -67,7 +67,7 @@ tvheadend.iptv = function(adapterId) { { header : "Interface", dataIndex : 'interface', - width : 100, + width : 50, renderer : function(value, metadata, record, row, col, store) { return value ? value : 'Unset'; }, @@ -76,9 +76,9 @@ tvheadend.iptv = function(adapterId) { }) }, { - header : "Group", + header : "Address / Group", dataIndex : 'group', - width : 100, + width : 150, renderer : function(value, metadata, record, row, col, store) { return value ? value : 'Unset'; }, @@ -89,7 +89,7 @@ tvheadend.iptv = function(adapterId) { { header : "UDP Port", dataIndex : 'port', - width : 60, + width : 50, editor : new fm.NumberField({ minValue : 1, maxValue : 65535