diff --git a/src/input.c b/src/input.c
new file mode 100644
index 00000000..38ce1dd6
--- /dev/null
+++ b/src/input.c
@@ -0,0 +1,28 @@
+/*
+ * Tvheadend
+ * 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 "input.h"
+
+void input_init ( void )
+{
+#if ENABLE_MPEGTS
+#if ENABLE_IPTV
+ iptv_input_init();
+#endif
+#endif
+}
diff --git a/src/input/mpegts/dvb.h b/src/input/mpegts/dvb.h
new file mode 100644
index 00000000..1b679452
--- /dev/null
+++ b/src/input/mpegts/dvb.h
@@ -0,0 +1,80 @@
+/*
+ * TV Input - Linux DVB interface - Support
+ * Copyright (C) 2007 Andreas Öman
+ *
+ * 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 .
+ */
+
+/*
+ * Based on:
+ *
+ * ITU-T Recommendation H.222.0 / ISO standard 13818-1
+ * EN 300 468 - V1.7.1
+ */
+
+#ifndef DVB_SUPPORT_H
+#define DVB_SUPPORT_H
+
+#define DVB_DESC_VIDEO_STREAM 0x02
+#define DVB_DESC_REGISTRATION 0x05
+#define DVB_DESC_CA 0x09
+#define DVB_DESC_LANGUAGE 0x0a
+
+/* Descriptors defined in EN 300 468 */
+
+#define DVB_DESC_NETWORK_NAME 0x40
+#define DVB_DESC_SERVICE_LIST 0x41
+#define DVB_DESC_SAT 0x43
+#define DVB_DESC_CABLE 0x44
+#define DVB_DESC_SHORT_EVENT 0x4d
+#define DVB_DESC_EXT_EVENT 0x4e
+#define DVB_DESC_SERVICE 0x48
+#define DVB_DESC_COMPONENT 0x50
+#define DVB_DESC_CONTENT 0x54
+#define DVB_DESC_PARENTAL_RAT 0x55
+#define DVB_DESC_TELETEXT 0x56
+#define DVB_DESC_SUBTITLE 0x59
+#define DVB_DESC_TERR 0x5a
+#define DVB_DESC_AC3 0x6a
+#define DVB_DESC_DEF_AUTHORITY 0x73
+#define DVB_DESC_CRID 0x76
+#define DVB_DESC_EAC3 0x7a
+#define DVB_DESC_AAC 0x7c
+#define DVB_DESC_LOCAL_CHAN 0x83
+
+typedef struct dvb_string_conv
+{
+ uint8_t type;
+ size_t (*func) ( char *dst, size_t *dstlen,
+ const uint8_t* src, size_t srclen );
+} dvb_string_conv_t;
+
+int dvb_get_string(char *dst, size_t dstlen, const uint8_t *src,
+ const size_t srclen, const char *dvb_charset,
+ dvb_string_conv_t *conv);
+
+int dvb_get_string_with_len(char *dst, size_t dstlen,
+ const uint8_t *buf, size_t buflen, const char *dvb_charset,
+ dvb_string_conv_t *conv);
+
+#define bcdtoint(i) ((((i & 0xf0) >> 4) * 10) + (i & 0x0f))
+
+time_t dvb_convert_date(uint8_t *dvb_buf);
+
+const char *dvb_polarisation_to_str(int pol);
+const char *dvb_polarisation_to_str_long(int pol);
+
+void atsc_utf16_to_utf8(uint8_t *src, int len, char *buf, int buflen);
+
+#endif /* DVB_SUPPORT_H */
diff --git a/src/input/mpegts/iptv.h b/src/input/mpegts/iptv.h
new file mode 100644
index 00000000..34cc8b79
--- /dev/null
+++ b/src/input/mpegts/iptv.h
@@ -0,0 +1,31 @@
+/*
+ * IPTV Input
+ *
+ * Copyright (C) 2013 Andreas Ă–man
+ *
+ * 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 .
+ */
+
+#ifndef __IPTV_H__
+#define __IPTV_H__
+
+void iptv_init ( void );
+
+#endif /* __IPTV_H__ */
+
+/******************************************************************************
+ * Editor Configuration
+ *
+ * vim:sts=2:ts=2:sw=2:et
+ *****************************************************************************/
diff --git a/src/input/mpegts/iptv/iptv.c b/src/input/mpegts/iptv/iptv.c
new file mode 100644
index 00000000..2a5b3ad7
--- /dev/null
+++ b/src/input/mpegts/iptv/iptv.c
@@ -0,0 +1,302 @@
+/*
+ * IPTV Input
+ *
+ * Copyright (C) 2013 Andreas Ă–man
+ *
+ * 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 "iptv_private.h"
+#include "tcp.h"
+
+#include
+#include
+#include
+#include
+
+
+/*
+ * Globals
+ */
+iptv_input_t iptv_input;
+iptv_network_t iptv_network;
+int iptv_poll_fd;
+pthread_t iptv_thread;
+pthread_mutex_t iptv_lock;
+
+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;
+ }
+
+ return 0;
+}
+
+static int http_connect (const char *urlstr, htsbuf_queue_t *spill)
+{
+ int fd, c;
+ char buf[1024];
+ url_t url;
+ if (url_parse(&url, urlstr))
+ return -1;
+
+ /* Make connection */
+ fd = tcp_connect(url.host, url.port, buf, sizeof(buf), 10);
+ if (!fd)
+ return -1;
+
+ /* Send request (VERY basic) */
+ c = snprintf(buf, sizeof(buf), "GET %s HTTP/1.1\r\n", urlstr);
+ 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 */
+ htsbuf_queue_flush(spill);
+ while ((c = tcp_read_line(fd, buf, sizeof(buf), spill)))
+ if (!*buf) break;
+
+ return fd;
+}
+
+/*
+ * HTTP
+ */
+static int
+iptv_input_start_http ( iptv_mux_t *im )
+{
+ int ret = SM_CODE_TUNING_FAILED;
+
+ /* Setup connection */
+ im->mm_iptv_fd = http_connect(im->mm_iptv_url, &im->mm_iptv_spill);
+
+ return ret;
+}
+
+/*
+ * Input definition
+ */
+extern const idclass_t mpegts_input_class;
+const idclass_t iptv_input_class = {
+ .ic_super = &mpegts_input_class,
+ .ic_class = "iptv_input",
+ .ic_caption = "IPTV Input",
+ .ic_properties = (const property_t[]){
+ }
+};
+
+static int
+iptv_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi )
+{
+ int ret = SM_CODE_TUNING_FAILED;
+ iptv_mux_t *im = (iptv_mux_t*)mmi->mmi_mux;
+ assert(mmi == &im->mm_iptv_instance);
+
+ pthread_mutex_lock(&iptv_lock);
+ if (!im->mm_active) {
+
+ /* HTTP */
+ if (!strcmp(im->mm_iptv_url, "http"))
+ ret = iptv_input_start_http(im);
+
+ /* OK */
+ if (!ret) {
+ struct epoll_event ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.events = EPOLLIN;
+ ev.data.fd = im->mm_iptv_fd;
+ epoll_ctl(iptv_poll_fd, EPOLL_CTL_ADD, im->mm_iptv_fd, &ev);
+ im->mm_active = mmi;
+ }
+ }
+ pthread_mutex_unlock(&iptv_lock);
+
+ return ret;
+}
+
+static void
+iptv_input_stop_mux ( mpegts_input_t *mi )
+{
+}
+
+static int
+iptv_input_is_free ( mpegts_input_t *mi )
+{
+ return 1; // unlimited number of muxes
+}
+
+static int
+iptv_input_current_weight ( mpegts_input_t *mi )
+{
+ return 0; // unlimited number of muxes
+}
+
+static void *
+iptv_input_thread ( void *aux )
+{
+#if 0
+ int nfds, fd;
+ uint8_t tsb[65536];
+ struct epoll_event ev;
+ iptv_mux_t *im;
+ mpegts_mux_t *mm;
+
+ while ( 1 ) {
+ nfds = epoll_wait(iptv_poll_fd, &ev, 1, -1);
+ if ( nfds < 0 ) {
+ tvhlog(LOG_ERR, "iptv", "epoll() error %s, sleeping 1 second",
+ strerror(errno));
+ sleep(1);
+ continue;
+ } else if ( nfds == 0 ) {
+ continue;
+ }
+
+ /* Read data */
+ fd = ev.data.fd;
+ r = read(fd, tsb+c, sizeof(tsb)-c);
+
+ /* Error */
+ if (r < 0) {
+ tvhlog(LOG_ERR, "iptv", "read() error %s", strerror(errno));
+ // TODO: close and remove?
+ continue;
+ }
+
+ pthread_mutex_lock(&iptv_lock);
+
+ /* Find mux */
+ LIST_FOREACH(mm, &iptv_network.mn_muxes, mm_network_link)
+ if (((iptv_mux_t*)mm)->im_fd == fd)
+ break;
+ if (!mm) {
+ pthread_mutex_unlock(&iptv_lock);
+ continue;
+ }
+ im = (iptv_mux_t*)mm;
+
+ /* Raw TS */
+ if (!strncmp(im->mm_iptv_url, "http", 4)) {
+ mpegts_input_recv_packets((mpegts_input_t*)&iptv_input,
+ &im->mm_mux_instance,
+ tsb, c, NULL, NULL);
+ }
+
+ pthread_mutex_lock(&iptv_unlock);
+ }
+#endif
+ return NULL;
+}
+
+/*
+ * Network definition
+ */
+extern const idclass_t mpegts_network_class;
+const idclass_t iptv_network_class = {
+ .ic_super = &mpegts_network_class,
+ .ic_class = "iptv_network",
+ .ic_caption = "IPTV Network",
+ .ic_properties = (const property_t[]){
+ }
+};
+
+static mpegts_service_t *
+iptv_network_create_service
+ ( mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid )
+{
+ return NULL;
+}
+
+/*
+ * Intialise and load config
+ */
+void iptv_init ( void )
+{
+ /* Init Input */
+#if 0
+ mpegts_input_init((mpegts_input_t*)&iptv_input,
+ &itpv_input_class, NULL);
+#endif
+ iptv_input.mi_start_mux = iptv_input_start_mux;
+ iptv_input.mi_stop_mux = iptv_input_stop_mux;
+ iptv_input.mi_is_free = iptv_input_is_free;
+ iptv_input.mi_current_weight = iptv_input_current_weight;
+#if 0 // Use defaults
+ iptv_input.mi_open_service = iptv_input_open_service;
+ iptv_input.mi_close_sevice = iptv_input_close_service;
+#endif
+
+ /* Init Network */
+#if 0
+ mpegts_network_init((mpegts_network_t*)&iptv_network,
+ &iptv_network_class, NULL);
+#endif
+ iptv_network.mn_network_name = strdup("IPTV Network");
+ iptv_network.mn_create_service = iptv_network_create_service;
+
+ /* Link */
+ iptv_input.mi_network = (mpegts_network_t*)&iptv_network;
+ //iptv_network.mn_input = (mpegts_input_t*)&iptv_input;
+
+ /* Setup thread */
+ // TODO: could set this up only when needed
+ iptv_poll_fd = epoll_create(10);
+ pthread_mutex_init(&iptv_lock, NULL);
+ pthread_create(&iptv_thread, NULL, iptv_input_thread, NULL);
+
+ /* Load config */
+ iptv_mux_load_all();
+}
+
+
+/******************************************************************************
+ * Editor Configuration
+ *
+ * vim:sts=2:ts=2:sw=2:et
+ *****************************************************************************/
diff --git a/src/input/mpegts/iptv/iptv_mux.c b/src/input/mpegts/iptv/iptv_mux.c
new file mode 100644
index 00000000..a9d5f60d
--- /dev/null
+++ b/src/input/mpegts/iptv/iptv_mux.c
@@ -0,0 +1,113 @@
+/*
+ * IPTV Input
+ *
+ * Copyright (C) 2013 Andreas Ă–man
+ *
+ * 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 "iptv_private.h"
+#include "settings.h"
+
+/*
+ * Class
+ */
+extern const idclass_t mpegts_mux_class;
+extern const idclass_t mpegts_mux_instance_class;
+
+const idclass_t iptv_mux_class =
+{
+ .ic_super = &mpegts_mux_class,
+ .ic_class = "iptv_mux",
+ .ic_caption = "IPTV Multiplex",
+ .ic_properties = (const property_t[]){
+ { PROPDEF1("iptv_url", "URL",
+ PT_STR, iptv_mux_t, mm_iptv_url) },
+#if 0
+ { PROPDEF1("iptv_interface", "Interface",
+ PT_STR, iptv_mux_t, mm_iptv_interface) },
+#endif
+ }
+};
+
+/*
+ * Create
+ */
+iptv_mux_t *
+iptv_mux_create ( const char *uuid, const char *url )
+{
+ /* Create Mux */
+ iptv_mux_t *im =
+ mpegts_mux_create(iptv_mux, NULL,
+ (mpegts_network_t*)&iptv_network,
+ MM_ONID_NONE, MM_TSID_NONE);
+ if (url)
+ im->mm_iptv_url = strdup(url);
+ htsbuf_queue_init(&im->mm_iptv_spill, 65536);
+
+ /* Create Instance */
+ mpegts_mux_instance_create0(&im->mm_iptv_instance,
+ &mpegts_mux_instance_class,
+ NULL,
+ (mpegts_input_t*)&iptv_input,
+ (mpegts_mux_t*)im);
+
+ return im;
+}
+
+/*
+ * Load
+ */
+static void
+iptv_mux_load_one ( iptv_mux_t *im, htsmsg_t *c )
+{
+ const char *str;
+
+ /* Load core */
+ mpegts_mux_load_one((mpegts_mux_t*)im, c);
+
+ /* URL */
+ if ((str = htsmsg_get_str(c, "url")))
+ tvh_str_update(&im->mm_iptv_url, str);
+}
+
+void
+iptv_mux_load_all ( void )
+{
+ htsmsg_t *s, *m;
+ htsmsg_field_t *f;
+ iptv_mux_t *im;
+
+ if ((s = hts_settings_load_r(1, "input/mpegts/iptv/muxes"))) {
+ HTSMSG_FOREACH(f, s) {
+ if (!(m = htsmsg_get_map_by_field(f))) {
+ tvhlog(LOG_ERR, "iptv", "failed to load mux config %s", f->hmf_name);
+ continue;
+ }
+
+ /* 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);
+ }
+ }
+}
diff --git a/src/input/mpegts/iptv/iptv_private.h b/src/input/mpegts/iptv/iptv_private.h
new file mode 100644
index 00000000..0ac7916b
--- /dev/null
+++ b/src/input/mpegts/iptv/iptv_private.h
@@ -0,0 +1,74 @@
+/*
+ * IPTV Input
+ *
+ * Copyright (C) 2013 Andreas Ă–man
+ *
+ * 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 .
+ */
+
+#ifndef __IPTV_PRIVATE_H__
+#define __IPTV_PRIVATE_H__
+
+#include "input/mpegts.h"
+#include "input/mpegts/iptv.h"
+#include "htsbuf.h"
+
+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;
+
+struct iptv_input
+{
+ mpegts_input_t;
+};
+
+struct iptv_network
+{
+ mpegts_network_t;
+};
+
+struct iptv_mux
+{
+ mpegts_mux_t;
+
+ int mm_iptv_fd;
+ htsbuf_queue_t mm_iptv_spill;
+ mpegts_mux_instance_t mm_iptv_instance;
+ char *mm_iptv_url;
+};
+
+iptv_mux_t* iptv_mux_create ( const char *uuid, const char *url );
+
+struct iptv_service
+{
+ mpegts_service_t;
+};
+
+iptv_service_t *iptv_service_create
+ ( const char *uuid, iptv_mux_t *im, uint16_t sid, uint16_t pmt_pid );
+
+extern iptv_input_t iptv_input;
+extern iptv_network_t iptv_network;
+
+void iptv_mux_load_all ( void );
+void iptv_service_load_all ( iptv_mux_t *im, const char *n );
+
+#endif /* __IPTV_PRIVATE_H__ */
+
+/******************************************************************************
+ * Editor Configuration
+ *
+ * vim:sts=2:ts=2:sw=2:et
+ *****************************************************************************/
diff --git a/src/input/mpegts/iptv/iptv_service.c b/src/input/mpegts/iptv/iptv_service.c
new file mode 100644
index 00000000..6ea965d2
--- /dev/null
+++ b/src/input/mpegts/iptv/iptv_service.c
@@ -0,0 +1,74 @@
+/*
+ * IPTV Input
+ *
+ * Copyright (C) 2013 Andreas Ă–man
+ *
+ * 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 "iptv_private.h"
+#include "settings.h"
+
+extern const idclass_t mpegts_service_class;
+
+/*
+ * Create
+ */
+iptv_service_t *
+iptv_service_create
+ ( const char *uuid, iptv_mux_t *im, uint16_t onid, uint16_t tsid )
+{
+ iptv_service_t *is = (iptv_service_t*)
+ mpegts_service_create0(calloc(1, sizeof(mpegts_service_t)),
+ &mpegts_service_class, uuid,
+ (mpegts_mux_t*)im, onid, tsid);
+ return is;
+}
+
+/*
+ * Load
+ */
+static void
+iptv_service_load_one ( iptv_service_t *is, htsmsg_t *c )
+{
+ /* Load core */
+ mpegts_service_load_one((mpegts_service_t*)is, c);
+}
+
+void
+iptv_service_load_all ( iptv_mux_t *im, const char *n )
+{
+ htsmsg_t *s, *c;
+ htsmsg_field_t *f;
+ iptv_service_t *is;
+
+ if ((s = hts_settings_load_r(1, "input/mpegts/iptv/muxes/%s/services", n))) {
+ HTSMSG_FOREACH(f, s) {
+ if (!(c = htsmsg_get_map_by_field(f))) {
+ tvhlog(LOG_ERR, "iptv", "failed to load svc config %s", f->hmf_name);
+ continue;
+ }
+
+ /* Create */
+ if (!(is = iptv_service_create(f->hmf_name, im, 0, 0))) {
+ tvhlog(LOG_ERR, "iptv", "failed to load svc config %s", f->hmf_name);
+ continue;
+ }
+
+ /* Load */
+ iptv_service_load_one(is, c);
+ }
+ }
+}
+