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); + } + } +} +