From: Jaroslav Kysela Date: Fri, 13 Feb 2015 15:06:09 +0000 (+0100) Subject: SAT>IP server: initial code X-Git-Tag: v4.1~318 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ea60066a3f63efb9d41ba6a653d01e6aeb7a9835;p=thirdparty%2Ftvheadend.git SAT>IP server: initial code --- diff --git a/Makefile b/Makefile index 82dce3d44..30fca816f 100644 --- a/Makefile +++ b/Makefile @@ -159,6 +159,10 @@ SRCS = src/version.c \ SRCS-${CONFIG_UPNP} += \ src/upnp.c +# SATIP Server +SRCS-${CONFIG_SATIP_SERVER} += \ + src/satip/server.c + SRCS += \ src/api.c \ src/api/api_status.c \ @@ -261,7 +265,7 @@ SRCS-${CONFIG_LINUXDVB} += \ src/input/mpegts/linuxdvb/linuxdvb_rotor.c \ src/input/mpegts/linuxdvb/linuxdvb_en50494.c -# SATIP +# SATIP Client SRCS-${CONFIG_SATIP_CLIENT} += \ src/input/mpegts/satip/satip.c \ src/input/mpegts/satip/satip_frontend.c \ diff --git a/configure b/configure index 39dc448fc..f72837245 100755 --- a/configure +++ b/configure @@ -21,6 +21,7 @@ OPTIONS=( "constcw:yes" "v4l:no" "linuxdvb:yes" + "satip_server:yes" "satip_client:yes" "hdhomerun_client:auto" "hdhomerun_static:no" @@ -257,6 +258,13 @@ if enabled_or_auto zlib; then fi fi +# +# SAT>IP server +# +if enabled_or_auto satip_server; then + enable upnp +fi + # # SAT>IP client # @@ -450,11 +458,12 @@ fi disable mpegts disable mpegps disable mpegts_dvb -if enabled linuxdvb || enabled iptv || enabled tsfile || enabled satip_client || enabled hdhomerun_client; +if enabled linuxdvb || enabled iptv || enabled tsfile || enabled satip_client || \ + enabled hdhomerun_client || enabled satip_server; then enable mpegts fi -if enabled linuxdvb || enabled satip_client || enabled hdhomerun_client; then +if enabled linuxdvb || enabled satip_client || enabled hdhomerun_client || enabled satip_server; then enable mpegts_dvb fi diff --git a/docs/html/config_misc.html b/docs/html/config_misc.html index 566e2b917..9e81c2436 100644 --- a/docs/html/config_misc.html +++ b/docs/html/config_misc.html @@ -131,5 +131,45 @@ +

+
+ SAT>IP Server +
+ +
SAT>IP Server is something like DVB network tuner. TVHeadend can + forward mpegts input streams including on-the-fly descramling to SAT>IP + clients.
+ +
+ +
RTSP Port +
+ Select RTSP port (TCP) for realtime commands from SAT>IP clients. Usually + (as defined in the specification) this port is 554. But as extension, + TVHeadend can use any TCP port value (which is default 9983 for non-root + users). But the SAT>IP client must allow to set this value (TVHeadend + client will obtain the RTSP port number automatically using the XML + description). If the RTSP port value is zero, the SAT>IP server + functionality is not enabled. + +
Subscription Weight +
+ Subscription weight value. Default value is 100 (standard streaming). Note + that the default value for DVR is 300 (normal priority). + +
Exported DVB-T Tuners +
+ Exported DVB-T tuners - streaming instances. + +
Exported DVB-S2 Tuners +
+ Exported DVB-S2 tuners - streaming instances. + +
Exported DVB-C Tuners +
+ Exported DVB-C tuners - streaming instances. + +
+ diff --git a/docs/html/config_networks.html b/docs/html/config_networks.html index bf5bd26b5..1c1221ee5 100644 --- a/docs/html/config_networks.html +++ b/docs/html/config_networks.html @@ -86,6 +86,11 @@ Buttons have the following functions:

Ignore Provider's Channel Numbers
Do not use the local channel numbers defined by provider. +

+

SAT>IP Source Number +
For DVB-S SAT>IP source means the LNB selection. 0 = do not use; 1 = AA; 2 = AB; 3 = BA; 4 = BB. + Other values are allowed too (but clients have to support them). + For other network types (DVB-T, DVB-C), use value 1 to enable SAT>IP for this network.

EIT Local Time
EPG (EIT) events uses local time instead UTC. diff --git a/src/input/mpegts.h b/src/input/mpegts.h index ef03e9bec..7e2559a66 100644 --- a/src/input/mpegts.h +++ b/src/input/mpegts.h @@ -293,6 +293,7 @@ struct mpegts_network * Configuration */ uint16_t mn_nid; + uint16_t mn_satip_source; int mn_autodiscovery; int mn_skipinitscan; char *mn_charset; diff --git a/src/input/mpegts/mpegts_network.c b/src/input/mpegts/mpegts_network.c index 4c0b90e79..32c1aad5c 100644 --- a/src/input/mpegts/mpegts_network.c +++ b/src/input/mpegts/mpegts_network.c @@ -188,6 +188,14 @@ const idclass_t mpegts_network_class = .off = offsetof(mpegts_network_t, mn_ignore_chnum), .def.i = 0, }, +#if ENABLE_SATIP_SERVER + { + .type = PT_U16, + .id = "satip_source", + .name = "SAT>IP Source Number", + .off = offsetof(mpegts_network_t, mn_satip_source), + }, +#endif { .type = PT_STR, .id = "charset", diff --git a/src/input/mpegts/satip/satip.c b/src/input/mpegts/satip/satip.c index 1d1c5eff5..49d9bfb44 100644 --- a/src/input/mpegts/satip/satip.c +++ b/src/input/mpegts/satip/satip.c @@ -23,6 +23,7 @@ #include "htsmsg_xml.h" #include "upnp.h" #include "settings.h" +#include "satip/server.h" #include "satip_private.h" #include "dbus.h" @@ -882,7 +883,7 @@ satip_discovery_service_received /* Sanity checks */ if (st == NULL || strcmp(st, "urn:ses-com:device:SatIPServer:1")) return; - if (uuid == NULL || strlen(uuid) < 16) + if (uuid == NULL || strlen(uuid) < 16 || satip_server_match_uuid(uuid)) return; if (location == NULL || strncmp(location, "http://", 7)) return; diff --git a/src/main.c b/src/main.c index 41e9dada0..6c21e192a 100644 --- a/src/main.c +++ b/src/main.c @@ -50,6 +50,7 @@ #include "descrambler.h" #include "dvr/dvr.h" #include "htsp_server.h" +#include "satip/server.h" #include "avahi.h" #include "bonjour.h" #include "input.h" @@ -142,6 +143,9 @@ const tvh_caps_t tvheadend_capabilities[] = { #if ENABLE_V4L || ENABLE_LINUXDVB || ENABLE_SATIP_CLIENT || ENABLE_HDHOMERUN_CLIENT { "tvadapters", NULL }, #endif +#if ENABLE_SATIP_SERVER + { "satip_server", NULL }, +#endif #if ENABLE_IMAGECACHE { "imagecache", (uint32_t*)&imagecache_conf.enabled }, #endif @@ -470,6 +474,9 @@ main(int argc, char **argv) opt_fileline = 0, opt_threadid = 0, opt_ipv6 = 0, +#if ENABLE_SATIP_SERVER + opt_satip_rtsp = 0, +#endif #if ENABLE_TSFILE opt_tsfile_tuner = 0, #endif @@ -522,6 +529,11 @@ main(int argc, char **argv) { 'a', "adapters", "Only use specified DVB adapters (comma separated)", OPT_STR, &opt_dvb_adapters }, #endif +#if ENABLE_SATIP_SERVER + { 0, "satip_rtsp", "SAT>IP RTSP port number for server\n" + "(default: -1 = disable, 0 = webconfig, standard port is 554)", + OPT_INT, &opt_satip_rtsp }, +#endif #if ENABLE_SATIP_CLIENT { 0, "satip_xml", "URL with the SAT>IP server XML location", OPT_STR_LIST, &opt_satip_xml }, @@ -696,9 +708,11 @@ main(int argc, char **argv) signal(SIGPIPE, handle_sigpipe); // will be redundant later signal(SIGILL, handle_sigill); // see handler.. + uuid_init(); tcp_server_preinit(opt_ipv6); - http_server_init(opt_bindaddr); // bind to ports only - htsp_init(opt_bindaddr); // bind to ports only + http_server_init(opt_bindaddr); // bind to ports only + htsp_init(opt_bindaddr); // bind to ports only + satip_server_init(opt_satip_rtsp); // bind to ports only if (opt_fork) pidfile = tvh_fopen(opt_pidpath, "w+"); @@ -801,7 +815,6 @@ main(int argc, char **argv) SSL_library_init(); /* Initialise configuration */ - uuid_init(); idnode_init(); spawn_init(); config_init(opt_config, opt_nobackup == 0); @@ -871,9 +884,9 @@ main(int argc, char **argv) dbus_server_start(); http_server_register(); + satip_server_register(); htsp_register(); - if(opt_subscribe != NULL) subscription_dummy_join(opt_subscribe, 1); @@ -914,6 +927,7 @@ main(int argc, char **argv) #if ENABLE_UPNP tvhftrace("main", upnp_server_done); #endif + tvhftrace("main", satip_server_done); tvhftrace("main", htsp_done); tvhftrace("main", http_server_done); tvhftrace("main", webui_done); diff --git a/src/satip/server.c b/src/satip/server.c new file mode 100644 index 000000000..3924a3948 --- /dev/null +++ b/src/satip/server.c @@ -0,0 +1,590 @@ +/* + * Tvheadend - SAT-IP server + * + * Copyright (C) 2015 Jaroslav Kysela + * + * 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 "input.h" +#include "htsbuf.h" +#include "htsmsg_xml.h" +#include "upnp.h" +#include "http.h" +#include "settings.h" +#include "config.h" +#include "satip/server.h" + +#include +#include + +#if defined(PLATFORM_FREEBSD) || ENABLE_ANDROID +#include +#include +#endif + +#define UPNP_MAX_AGE 1800 + +static char *http_server_ip; +static int http_server_port; +static char *satip_server_uuid; +static int satip_server_deviceid; +static time_t satip_server_bootid; +static int satip_server_rtsp_port; +static int satip_server_rtsp_port_locked; +static upnp_service_t *satips_upnp_discovery; + +/* + * + */ +int satip_server_match_uuid(const char *uuid) +{ + return strcmp(uuid ?: "", satip_server_uuid ?: "") == 0; +} + +/* + * XML description + */ + +static int +satip_server_http_xml(http_connection_t *hc) +{ +#define MSG "\ +\n\ +\n\ +11\n\ +\n\ +urn:ses-com:device:SatIPServer:1\n\ +TVHeadend %s\n\ +TVHeadend Team\n\ +http://tvheadend.org\n\ +TVHeadend %s\n\ +TVHeadend SAT>IP\n\ +1\n\ +http://tvheadend.org\n\ +123456\n\ +uuid:%s\n\ +TVHeadend %s {{{RTSP:%d;SRCS:%d}}}\n\ +\n\ +\n\ +image/png\n\ +40\n\ +40\n\ +16\n\ +http://%s:%d/static/satip-icon40.png\n\ +\n\ +\n\ +image/jpg\n\ +40\n\ +40\n\ +16\n\ +http://%s:%d/static/satip-icon40.jpg\n\ +\n\ +\n\ +image/png\n\ +120\n\ +120\n\ +16\n\ +http://%s:%d/static/satip-icon120.png\n\ +\n\ +\n\ +image/jpg\n\ +120\n\ +120\n\ +16\n\ +http://%s:%d/static/satip-icon120.jpg\n\ +\n\ +\n\ +http://%s:%d\n\ +%s\n\ +\n\ +\n" + + char buf[sizeof(MSG) + 1024]; + char *devicelist = NULL; + htsbuf_queue_t q; + mpegts_network_t *mn; + int dvbt = 0, dvbs = 0, dvbc = 0, delim = 0, i; + + htsbuf_queue_init(&q, 0); + + pthread_mutex_lock(&global_lock); + LIST_FOREACH(mn, &mpegts_network_all, mn_global_link) { + if (mn->mn_satip_source == 0) + continue; + if (idnode_is_instance(&mn->mn_id, &dvb_network_dvbt_class)) + dvbt++; + else if (idnode_is_instance(&mn->mn_id, &dvb_network_dvbs_class)) + dvbs++; + else if (idnode_is_instance(&mn->mn_id, &dvb_network_dvbc_class)) + dvbc++; + } + if (dvbt && (i = config_get_int("satip_dvbt", 0)) > 0) { + htsbuf_qprintf(&q, "DVBT-%d", i); + delim++; + } else { + dvbt = 0; + } + if (dvbs && (i = config_get_int("satip_dvbs", 0)) > 0) { + htsbuf_qprintf(&q, "%sDVBS2-%d", delim ? "," : "", i); + delim++; + } else { + dvbs = 0; + } + if (dvbc && (i = config_get_int("satip_dvbc", 0)) > 0) { + htsbuf_qprintf(&q, "%sDVBC-%d", delim ? "," : "", i); + delim++; + } else { + dvbc = 0; + } + pthread_mutex_unlock(&global_lock); + + devicelist = htsbuf_to_string(&q); + htsbuf_queue_flush(&q); + + if (devicelist == NULL || devicelist[0] == '\0') { + tcp_get_ip_str((struct sockaddr*)hc->hc_peer, buf, sizeof(buf)); + tvhwarn("satips", "SAT>IP server announces an empty tuner list to a client %s (missing %s)", + buf, dvbt + dvbs + dvbc ? "tuner settings - global config" : "network assignment"); + } + + snprintf(buf, sizeof(buf), MSG, + tvheadend_version, tvheadend_version, + satip_server_uuid, tvheadend_version, + satip_server_rtsp_port, dvbs, + http_server_ip, http_server_port, + http_server_ip, http_server_port, + http_server_ip, http_server_port, + http_server_ip, http_server_port, + http_server_ip, http_server_port, + devicelist ?: ""); + + free(devicelist); + + http_send_header(hc, 200, "text/xml", strlen(buf), 0, NULL, 10, 0, NULL); + tvh_write(hc->hc_fd, buf, strlen(buf)); + + return 0; +#undef MSG +} + +int +satip_server_http_page(http_connection_t *hc, + const char *remain, void *opaque) +{ + if (strcmp(remain, "desc.xml") == 0) + return satip_server_http_xml(hc); + return 0; +} + +/* + * Discovery + */ + +static void +satips_upnp_send_byebye(void) +{ +#define MSG "\ +NOTIFY * HTTP/1.1\r\n\ +HOST: 239.255.255.250:1900\r\n\ +NT: %s\r\n\ +NTS: ssdp:byebye\r\n\ +USN: uuid:%s%s\r\n\ +BOOTID.UPNP.ORG: %ld\r\n\ +CONFIGID.UPNP.ORG: 0\r\n\ +\r\n\r\n" + + int attempt; + char buf[512], buf2[50]; + const char *nt, *usn2; + htsbuf_queue_t q; + + if (satips_upnp_discovery == NULL || satip_server_rtsp_port <= 0) + return; + + tvhtrace("satips", "sending byebye"); + + for (attempt = 1; attempt < 3; attempt++) { + switch (attempt) { + case 1: + nt = "upnp:rootdevice"; + usn2 = "::upnp:rootdevice"; + break; + case 2: + snprintf(buf2, sizeof(buf2), "uuid:%s", satip_server_uuid); + nt = buf2; + usn2 = ""; + break; + case 3: + nt = "urn:ses-com:device:SatIPServer:1"; + usn2 = "::urn:ses-com:device:SatIPServer:1"; + break; + default: + abort(); + } + + snprintf(buf, sizeof(buf), MSG, nt, satip_server_uuid, + usn2, (long)satip_server_bootid); + + htsbuf_queue_init(&q, 0); + htsbuf_append(&q, buf, strlen(buf)); + upnp_send(&q, NULL, attempt * 11); + htsbuf_queue_flush(&q); + } +#undef MSG +} + +static void +satips_upnp_send_announce(void) +{ +#define MSG "\ +NOTIFY * HTTP/1.1\r\n\ +HOST: 239.255.255.250:1900\r\n\ +CACHE-CONTROL: max-age=%d\r\n\ +LOCATION: http://%s:%i/satip_server/desc.xml\r\n\ +NT: %s\r\n\ +NTS: ssdp:alive\r\n\ +SERVER: unix/1.0 UPnP/1.1 TVHeadend/%s\r\n\ +USN: uuid:%s%s\r\n\ +BOOTID.UPNP.ORG: %ld\r\n\ +CONFIGID.UPNP.ORG: 0\r\n\ +DEVICEID.SES.COM: %d\r\n\r\n" + + int attempt; + char buf[512], buf2[50]; + const char *nt, *usn2; + htsbuf_queue_t q; + + if (satips_upnp_discovery == NULL || satip_server_rtsp_port <= 0) + return; + + tvhtrace("satips", "sending announce"); + + for (attempt = 1; attempt < 3; attempt++) { + switch (attempt) { + case 1: + nt = "upnp:rootdevice"; + usn2 = "::upnp:rootdevice"; + break; + case 2: + snprintf(buf2, sizeof(buf2), "uuid:%s", satip_server_uuid); + nt = buf2; + usn2 = ""; + break; + case 3: + nt = "urn:ses-com:device:SatIPServer:1"; + usn2 = "::urn:ses-com:device:SatIPServer:1"; + break; + default: + abort(); + } + + snprintf(buf, sizeof(buf), MSG, UPNP_MAX_AGE, + http_server_ip, http_server_port, nt, tvheadend_version, + satip_server_uuid, usn2, (long)satip_server_bootid, + satip_server_deviceid); + + htsbuf_queue_init(&q, 0); + htsbuf_append(&q, buf, strlen(buf)); + upnp_send(&q, NULL, attempt * 11); + htsbuf_queue_flush(&q); + } +#undef MSG +} + +static void +satips_upnp_send_discover_reply + (struct sockaddr_storage *dst, const char *deviceid) +{ +#define MSG "\ +HTTP/1.1 200 OK\r\n\ +CACHE-CONTROL: max-age=%d\r\n\ +EXT:\r\n\ +LOCATION: http://%s:%i/satip_server/desc.xml\r\n\ +SERVER: unix/1.0 UPnP/1.1 TVHeadend/%s\r\n\ +ST: urn:ses-com:device:SatIPServer:1\r\n\ +USN: uuid:%s::urn:ses-com:device:SatIPServer:1\r\n\ +BOOTID.UPNP.ORG: %ld\r\n\ +CONFIGID.UPNP.ORG: 0\r\n" + + char buf[512]; + htsbuf_queue_t q; + struct sockaddr_storage storage; + + if (satips_upnp_discovery == NULL || satip_server_rtsp_port <= 0) + return; + +#if ENABLE_TRACE + tcp_get_ip_str((struct sockaddr *)dst, buf, sizeof(buf)); + tvhtrace("satips", "sending discover reply to %s:%d%s%s", + buf, IP_PORT(*dst), deviceid ? " device: " : "", deviceid ?: ""); +#endif + + snprintf(buf, sizeof(buf), MSG, UPNP_MAX_AGE, + http_server_ip, http_server_port, tvheadend_version, + satip_server_uuid, (long)satip_server_bootid); + + htsbuf_queue_init(&q, 0); + htsbuf_append(&q, buf, strlen(buf)); + if (deviceid) + htsbuf_qprintf(&q, "DEVICEID.SES.COM: %s", deviceid); + htsbuf_append(&q, "\r\n", 2); + storage = *dst; + upnp_send(&q, &storage, 0); + htsbuf_queue_flush(&q); +#undef MSG +} + +static void +satips_upnp_discovery_received + (uint8_t *data, size_t len, udp_connection_t *conn, + struct sockaddr_storage *storage) +{ +#define MSEARCH "M-SEARCH * HTTP/1.1" + + char *buf, *ptr, *saveptr; + char *argv[10]; + char *st = NULL, *man = NULL, *host = NULL, *deviceid = NULL, *searchport = NULL; + char buf2[64]; + + if (satip_server_rtsp_port <= 0) + return; + + if (len < 32 || len > 8191) + return; + + if (strncmp((char *)data, MSEARCH, sizeof(MSEARCH)-1)) + return; + +#undef MSEARCH + + buf = alloca(len+1); + memcpy(buf, data, len); + buf[len] = '\0'; + ptr = strtok_r(buf, "\r\n", &saveptr); + /* Request decoder */ + if (ptr) { + if (http_tokenize(ptr, argv, 3, -1) != 3) + return; + if (conn->multicast) { + if (strcmp(argv[0], "M-SEARCH")) + return; + if (strcmp(argv[1], "*")) + return; + if (strcmp(argv[2], "HTTP/1.1")) + return; + } else { + if (strcmp(argv[0], "HTTP/1.1")) + return; + if (strcmp(argv[1], "200")) + return; + } + ptr = strtok_r(NULL, "\r\n", &saveptr); + } + /* Header decoder */ + while (1) { + if (ptr == NULL) + break; + if (http_tokenize(ptr, argv, 2, ':') == 2) { + if (strcmp(argv[0], "ST") == 0) + st = argv[1]; + else if (strcmp(argv[0], "HOST") == 0) + host = argv[1]; + else if (strcmp(argv[0], "MAN") == 0) + man = argv[1]; + else if (strcmp(argv[0], "DEVICEID.SES.COM") == 0) + deviceid = argv[1]; + else if (strcmp(argv[0], "SEARCHPORT.UPNP.ORG") == 0) + searchport = argv[1]; + } + ptr = strtok_r(NULL, "\r\n", &saveptr); + } + /* Validation */ + if (searchport && strcmp(searchport, "1900")) + return; + if (st == NULL || strcmp(st, "urn:ses-com:device:SatIPServer:1")) + return; + if (man == NULL || strcmp(man, "\"ssdp:discover\"")) + return; + if (deviceid && atoi(deviceid) != satip_server_deviceid) + return; + if (host == NULL) + return; + if (http_tokenize(host, argv, 2, ':') != 2) + return; + if (strcmp(argv[1], "1900")) + return; + if (conn->multicast && strcmp(argv[0], "239.255.255.250")) + return; + if (!conn->multicast && strcmp(argv[0], http_server_ip)) + return; + +#if ENABLE_TRACE + tcp_get_ip_str((struct sockaddr *)storage, buf2, sizeof(buf2)); + tvhtrace("satips", "received %s M-SEARCH from %s:%d", + conn->multicast ? "multicast" : "unicast", + buf2, ntohs(IP_PORT(*storage))); +#endif + + /* Check for deviceid collision */ + if (!conn->multicast) { + if (deviceid) { + satip_server_deviceid += 1; + if (satip_server_deviceid >= 254) + satip_server_deviceid = 1; + tcp_get_ip_str((struct sockaddr *)storage, buf2, sizeof(buf2)); + tvhwarn("satips", "received duplicate SAT>IP DeviceID %s from %s:%d, using %d", + deviceid, buf2, ntohs(IP_PORT(*storage)), satip_server_deviceid); + satips_upnp_send_discover_reply(storage, deviceid); + satips_upnp_send_byebye(); + satips_upnp_send_announce(); + } else { + satips_upnp_send_discover_reply(storage, NULL); + } + } else { + satips_upnp_send_discover_reply(storage, NULL); + } +} + +static void +satips_upnp_discovery_destroy(upnp_service_t *upnp) +{ + satips_upnp_discovery = NULL; +} + +/* + * + */ +void satip_server_config_changed(void) +{ + int rtsp_port; + + if (!satip_server_rtsp_port_locked) { + rtsp_port = config_get_int("satip_rtsp", 0); + satip_server_rtsp_port = rtsp_port; + if (rtsp_port <= 0) { + tvhinfo("satips", "SAT>IP Server reinitialized (HTTP %s:%d, RTSP %s:%d, DVB-T %d, DVB-S2 %d, DVB-C %d)", + http_server_ip, http_server_port, http_server_ip, rtsp_port, + config_get_int("satip_dvbt", 0), + config_get_int("satip_dvbs", 0), + config_get_int("satip_dvbc", 0)); + satips_upnp_send_announce(); + } else { + tvhinfo("satips", "SAT>IP Server shutdown"); + satips_upnp_send_byebye(); + } + } +} + +/* + * Initialization + */ + +void satip_server_init(int rtsp_port) +{ + struct sockaddr_storage http; + char http_ip[128]; + + http_server_ip = NULL; + satip_server_bootid = time(NULL); + satip_server_uuid = NULL; + satip_server_deviceid = 1; + + if (tcp_server_bound(http_server, &http) < 0) { + tvherror("satips", "Unable to determine the HTTP/RTSP address"); + return; + } + tcp_get_ip_str((const struct sockaddr *)&http, http_ip, sizeof(http_ip)); + http_server_ip = strdup(http_ip); + http_server_port = ntohs(IP_PORT(http)); + + satip_server_rtsp_port_locked = 1; + if (rtsp_port == 0) { + satip_server_rtsp_port_locked = 0; + rtsp_port = config_get_int("satip_rtsp", getuid() == 0 ? 554 : 9983); + } + satip_server_rtsp_port = rtsp_port; + if (rtsp_port <= 0) + return; + + tvhinfo("satips", "SAT>IP Server initialized (HTTP %s:%d, RTSP %s:%d)", + http_server_ip, http_server_port, http_server_ip, rtsp_port); +} + +void satip_server_register(void) +{ + char uu[UUID_HEX_SIZE + 4]; + tvh_uuid_t u; + int save = 0; + + if (http_server_ip == NULL) + return; + + if (config_set_int("satip_rtsp", satip_server_rtsp_port)) + save = 1; + + if (config_get_int("satip_weight", 0) <= 0) + if (config_set_int("satip_weight", 100)) + save = 1; + + satip_server_deviceid = config_get_int("satip_deviceid", 0); + if (satip_server_deviceid <= 0) { + satip_server_deviceid = 1; + if (config_set_int("satip_deviceid", 1)) + save = 1; + } + + satip_server_uuid = (char *)config_get_str("satip_uuid"); + if (satip_server_uuid == NULL) { + /* This is not UPnP complaint UUID */ + if (uuid_init_bin(&u, NULL)) { + tvherror("satips", "Unable to create UUID"); + return; + } + bin2hex(uu + 0, 9, u.bin, 4); uu[ 8] = '-'; + bin2hex(uu + 9, 5, u.bin + 4, 2); uu[13] = '-'; + bin2hex(uu + 14, 5, u.bin + 6, 2); uu[18] = '-'; + bin2hex(uu + 19, 5, u.bin + 8, 2); uu[23] = '-'; + bin2hex(uu + 24, 9, u.bin + 10, 6); uu[36] = 0; + if (config_set_str("satip_uuid", uu)) + save = 1; + satip_server_uuid = uu; + } + + satip_server_uuid = strdup(satip_server_uuid); + + if (save) + config_save(); + + satips_upnp_discovery = upnp_service_create(upnp_service); + if (satips_upnp_discovery == NULL) { + tvherror("satips", "unable to create UPnP discovery service"); + } else { + satips_upnp_discovery->us_received = satips_upnp_discovery_received; + satips_upnp_discovery->us_destroy = satips_upnp_discovery_destroy; + } + + satips_upnp_send_announce(); +} + +void satip_server_done(void) +{ + if (satip_server_rtsp_port > 0) + satips_upnp_send_byebye(); + free(http_server_ip); + http_server_ip = NULL; + free(satip_server_uuid); + satip_server_uuid = NULL; +} diff --git a/src/satip/server.h b/src/satip/server.h new file mode 100644 index 000000000..41cb18571 --- /dev/null +++ b/src/satip/server.h @@ -0,0 +1,59 @@ +/* + * Tvheadend - SAT-IP DVB server - private data + * + * Copyright (C) 2015 Jaroslav Kysela + * + * 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 __TVH_SATIP_SERVER__H__ +#define __TVH_SATIP_SERVER_H__ + +#include "build.h" + +#if ENABLE_SATIP_SERVER + +#include "input.h" +#include "htsbuf.h" +#include "udp.h" +#include "http.h" + +void satip_server_rtsp_init(const char *bindaddr, int port); +void satip_server_rtsp_register(void); +void satip_server_rtsp_done(void); + +int satip_server_http_page(http_connection_t *hc, + const char *remain, void *opaque); + +int satip_server_match_uuid(const char *uuid); + +void satip_server_config_changed(void); + +void satip_server_init(int rtsp_port); +void satip_server_register(void); +void satip_server_done(void); + +#else + +static inline int satip_server_match_uuid(const char *uuid) { return 0; } + +static inline void satip_server_config_changed(void) { }; + +static inline void satip_server_init(int rtsp_port) { }; +static inline void satip_server_register(void) { }; +static inline void satip_server_done(void) { }; + +#endif + +#endif /* __TVH_SATIP_SERVER_H__ */ diff --git a/src/webui/extjs.c b/src/webui/extjs.c index cfe29c73a..fc2e04c10 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -47,6 +47,7 @@ #include "timeshift.h" #include "tvhtime.h" #include "input.h" +#include "satip/server.h" /** * @@ -510,7 +511,7 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) /* Save settings */ } else if (!strcmp(op, "saveSettings") ) { - int save = 0; + int save = 0, ssave = 0; /* Misc settings */ pthread_mutex_lock(&global_lock); @@ -524,8 +525,20 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) save |= config_set_chicon_path(str); if ((str = http_arg_get(&hc->hc_req_args, "piconpath"))) save |= config_set_picon_path(str); - if (save) + if ((str = http_arg_get(&hc->hc_req_args, "satip_rtsp"))) + ssave |= config_set_int("satip_rtsp", atoi(str)); + if ((str = http_arg_get(&hc->hc_req_args, "satip_weight"))) + ssave |= config_set_int("satip_weight", atoi(str)); + if ((str = http_arg_get(&hc->hc_req_args, "satip_dvbt"))) + ssave |= config_set_int("satip_dvbt", atoi(str)); + if ((str = http_arg_get(&hc->hc_req_args, "satip_dvbs"))) + ssave |= config_set_int("satip_dvbt", atoi(str)); + if ((str = http_arg_get(&hc->hc_req_args, "satip_dvbc"))) + ssave |= config_set_int("satip_dvbt", atoi(str)); + if (save | ssave) config_save(); + if (ssave) + satip_server_config_changed(); /* Time */ str = http_arg_get(&hc->hc_req_args, "tvhtime_update_enabled"); diff --git a/src/webui/static/app/config.js b/src/webui/static/app/config.js index 7f92254e0..71d05606a 100644 --- a/src/webui/static/app/config.js +++ b/src/webui/static/app/config.js @@ -44,9 +44,8 @@ tvheadend.miscconf = function(panel, index) { 'muxconfpath', 'language', 'tvhtime_update_enabled', 'tvhtime_ntp_enabled', 'tvhtime_tolerance', - 'prefer_picon', - 'chiconpath', - 'piconpath' + 'prefer_picon', 'chiconpath', 'piconpath', + 'satip_rtsp', 'satip_weight', 'satip_dvbt', 'satip_dvbs', 'satip_dvbc' ]); /* **************************************************************** @@ -216,6 +215,42 @@ tvheadend.miscconf = function(panel, index) { var imagecache_form = null; } + /* + * SAT>IP server + */ + + var satipPanel = null; + if (tvheadend.capabilities.indexOf('satip_server') !== -1) { + var rtsp = new Ext.form.NumberField({ + name: 'satip_rtsp', + fieldLabel: 'RTSP Port (554), 0 = disable' + }); + var weight = new Ext.form.NumberField({ + name: 'satip_weight', + fieldLabel: 'Subscription Weight' + }); + var dvbt = new Ext.form.NumberField({ + name: 'satip_dvbt', + fieldLabel: 'Exported DVB-T Tuners' + }); + var dvbs = new Ext.form.NumberField({ + name: 'satip_dvbt', + fieldLabel: 'Exported DVB-S2 Tuners' + }); + var dvbc = new Ext.form.NumberField({ + name: 'satip_dvbt', + fieldLabel: 'Exported DVB-C Tuners' + }); + satipPanel = new Ext.form.FieldSet({ + title: 'SAT>IP Server', + width: 700, + autoHeight: true, + collapsible: true, + animCollapse: true, + items: [rtsp, weight, dvbt, dvbs, dvbc] + }); + } + /* **************************************************************** * Form * ***************************************************************/ @@ -242,6 +277,11 @@ tvheadend.miscconf = function(panel, index) { } }); + var _items = [languageWrap, dvbscanWrap, tvhtimePanel, piconPanel]; + + if (satipPanel) + _items.push(satipPanel); + var confpanel = new Ext.form.FormPanel({ labelAlign: 'left', labelWidth: 200, @@ -251,7 +291,7 @@ tvheadend.miscconf = function(panel, index) { layout: 'form', defaultType: 'textfield', autoHeight: true, - items: [languageWrap, dvbscanWrap, tvhtimePanel, piconPanel] + items: _items }); var _items = [confpanel]; diff --git a/src/webui/webui.c b/src/webui/webui.c index 209cf0d23..9db3a87ad 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -47,6 +47,9 @@ #if ENABLE_MPEGTS #include "input.h" #endif +#if ENABLE_SATIP_SERVER +#include "satip/server.h" +#endif #if defined(PLATFORM_LINUX) #include @@ -1363,6 +1366,10 @@ webui_init(int xspf) http_path_add("/login", NULL, page_login, ACCESS_WEB_INTERFACE); http_path_add("/logout", NULL, page_logout, ACCESS_WEB_INTERFACE); +#if CONFIG_SATIP_SERVER + http_path_add("/satip_server", NULL, satip_server_http_page, ACCESS_ANONYMOUS); +#endif + http_path_add_modify("/play", NULL, page_play, ACCESS_ANONYMOUS, page_play_path_modify); http_path_add("/dvrfile", NULL, page_dvrfile, ACCESS_ANONYMOUS); http_path_add("/favicon.ico", NULL, favicon, ACCESS_WEB_INTERFACE); @@ -1382,7 +1389,6 @@ webui_init(int xspf) extjs_start(); comet_init(); webui_api_init(); - } void