]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
SAT>IP server: initial code
authorJaroslav Kysela <perex@perex.cz>
Fri, 13 Feb 2015 15:06:09 +0000 (16:06 +0100)
committerJaroslav Kysela <perex@perex.cz>
Wed, 11 Mar 2015 20:41:12 +0000 (21:41 +0100)
13 files changed:
Makefile
configure
docs/html/config_misc.html
docs/html/config_networks.html
src/input/mpegts.h
src/input/mpegts/mpegts_network.c
src/input/mpegts/satip/satip.c
src/main.c
src/satip/server.c [new file with mode: 0644]
src/satip/server.h [new file with mode: 0644]
src/webui/extjs.c
src/webui/static/app/config.js
src/webui/webui.c

index 82dce3d4451fc22406a8063dacef21ed31c34540..30fca816f2b1fe794706fa17f0c42d5706556274 100644 (file)
--- 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 \
index 39dc448fc2b7519f8b626dfd3565021d9db3ccee..f72837245f8e59ad13f5bfd080e35b94d708f516 100755 (executable)
--- 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
 
index 566e2b91715c10f88236921ed044d63cfd77742b..9e81c243675fae3a8f1ab379793eb07b3fade50e 100644 (file)
 
   </dl>
 
+  <br><br>
+  <hr>
+  <b>SAT>IP Server</b>
+  <hr>
+
+  <dd>SAT>IP Server is something like DVB network tuner. TVHeadend can
+  forward mpegts input streams including on-the-fly descramling to SAT>IP
+  clients.</dd>
+
+  <dl>
+
+  <dt>RTSP Port
+  <dd>
+  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.
+
+  <dt>Subscription Weight
+  <dd>
+  Subscription weight value. Default value is 100 (standard streaming). Note
+  that the default value for DVR is 300 (normal priority).
+
+  <dt>Exported DVB-T Tuners
+  <dd>
+  Exported DVB-T tuners - streaming instances.
+
+  <dt>Exported DVB-S2 Tuners
+  <dd>
+  Exported DVB-S2 tuners - streaming instances.
+
+  <dt>Exported DVB-C Tuners
+  <dd>
+  Exported DVB-C tuners - streaming instances.
+
+  </dl>
+
  </dl>  
 </div>
index bf5bd26b5dcee6a6014090079efc076f7850782e..1c1221ee56295d790325b58f9c660bbd2c73452d 100644 (file)
@@ -86,6 +86,11 @@ Buttons have the following functions:
 <p>
                <dt><b>Ignore Provider's Channel Numbers</b>
                <dd>Do not use the local channel numbers defined by provider.
+<p>
+               <dt><b>SAT>IP Source Number</b>
+               <dd>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.
 <p>
                <dt><b>EIT Local Time</b>
                <dd>EPG (EIT) events uses local time instead UTC.
index ef03e9bec994226b8e45f8ef7f0a53d8491a81c0..7e2559a6681de6dced9acb2ceea320379a3ee835 100644 (file)
@@ -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;
index 4c0b90e7932b07bd1641d939c426251e2b181474..32c1aad5cdbefc76b4b51b1de470b8a4607d03bc 100644 (file)
@@ -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",
index 1d1c5eff54e71035d3ed1ba8f473c985182f2bb5..49d9bfb44141bbec9a019e18e9bf0b3a1996a061 100644 (file)
@@ -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;
index 41e9dada081fe029c1a14124f5adab5b31e45162..6c21e192ad3fee1ca08c0b14228811341007678e 100644 (file)
@@ -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 (file)
index 0000000..3924a39
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <arpa/inet.h>
+#include <openssl/sha.h>
+
+#if defined(PLATFORM_FREEBSD) || ENABLE_ANDROID
+#include <sys/types.h>
+#include <sys/socket.h>
+#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 "\
+<?xml version=\"1.0\"?>\n\
+<root xmlns=\"urn:schemas-upnp-org:device-1-0\" configId=\"0\">\n\
+<specVersion><major>1</major><minor>1</minor></specVersion>\n\
+<device>\n\
+<deviceType>urn:ses-com:device:SatIPServer:1</deviceType>\n\
+<friendlyName>TVHeadend %s</friendlyName>\n\
+<manufacturer>TVHeadend Team</manufacturer>\n\
+<manufacturerURL>http://tvheadend.org</manufacturerURL>\n\
+<modelDescription>TVHeadend %s</modelDescription>\n\
+<modelName>TVHeadend SAT>IP</modelName>\n\
+<modelNumber>1</modelNumber>\n\
+<modelURL>http://tvheadend.org</modelURL>\n\
+<serialNumber>123456</serialNumber>\n\
+<UDN>uuid:%s</UDN>\n\
+<UPC>TVHeadend %s {{{RTSP:%d;SRCS:%d}}}</UPC>\n\
+<iconList>\n\
+<icon>\n\
+<mimetype>image/png</mimetype>\n\
+<width>40</width>\n\
+<height>40</height>\n\
+<depth>16</depth>\n\
+<url>http://%s:%d/static/satip-icon40.png</url>\n\
+</icon>\n\
+<icon>\n\
+<mimetype>image/jpg</mimetype>\n\
+<width>40</width>\n\
+<height>40</height>\n\
+<depth>16</depth>\n\
+<url>http://%s:%d/static/satip-icon40.jpg</url>\n\
+</icon>\n\
+<icon>\n\
+<mimetype>image/png</mimetype>\n\
+<width>120</width>\n\
+<height>120</height>\n\
+<depth>16</depth>\n\
+<url>http://%s:%d/static/satip-icon120.png</url>\n\
+</icon>\n\
+<icon>\n\
+<mimetype>image/jpg</mimetype>\n\
+<width>120</width>\n\
+<height>120</height>\n\
+<depth>16</depth>\n\
+<url>http://%s:%d/static/satip-icon120.jpg</url>\n\
+</icon>\n\
+</iconList>\n\
+<presentationURL>http://%s:%d</presentationURL>\n\
+<satip:X_SATIPCAP xmlns:satip=\"urn:ses-com:satip\">%s</satip:X_SATIPCAP>\n\
+</device>\n\
+</root>\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 (file)
index 0000000..41cb185
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#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__ */
index cfe29c73a8208184c8f12cd57d292b2462548298..fc2e04c10cfc7f09bacffb0aa72a956b9146db4f 100644 (file)
@@ -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");
index 7f92254e03f7321a6d5fded51331a7ef7677f5f1..71d05606a2bbbeb2b1a339301fa3c65c9edf3ebe 100644 (file)
@@ -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];
index 209cf0d2398f812fbadbfc122ba9c3c79bfd8050..9db3a87ad2b085b31944d5190cbdda1b54c083dc 100644 (file)
@@ -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 <sys/sendfile.h>
@@ -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