]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Work in progress on AJAX interface for tvheadend
authorAndreas Öman <andreas@lonelycoder.com>
Mon, 7 Apr 2008 15:57:20 +0000 (15:57 +0000)
committerAndreas Öman <andreas@lonelycoder.com>
Mon, 7 Apr 2008 15:57:20 +0000 (15:57 +0000)
39 files changed:
Makefile
ajaxui/ajaxui.c
ajaxui/ajaxui.css
ajaxui/ajaxui.h
ajaxui/ajaxui_config.c
ajaxui/ajaxui_config_channels.c [new file with mode: 0644]
ajaxui/ajaxui_config_dvb.c [new file with mode: 0644]
ajaxui/ajaxui_config_transport.c [new file with mode: 0644]
ajaxui/images/mapped.png [new file with mode: 0644]
ajaxui/images/sbbody_l.gif
ajaxui/images/sbbody_r.gif
ajaxui/images/sbhead_r.gif
ajaxui/images/unmapped.png [new file with mode: 0644]
ajaxui/prototype/prototype.js
ajaxui/tvheadend.js
avgen.c
channels.c
cwc.c
dvb.c
dvb.h
dvb_dvr.c
dvb_fe.c
dvb_muxconfig.c
dvb_muxconfig.h
dvb_support.c
dvb_support.h
dvb_tables.c
file_input.c
htsclient.c
http.c
iptv_input.c
main.c
psi.c
psi.h
transports.c
transports.h
tsdemux.c
tvhead.h
v4l.c

index bc368ef745f2cb645a98eb6c8c422a389ffe91c0..f94b2e4b942efd7076be9e6d5967934e671a8ce6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ SRCS = main.c dispatch.c channels.c transports.c teletext.c psi.c \
        subscriptions.c mux.c tsdemux.c buffer.c tcp.c \
        resolver.c tsmux.c parsers.c bitstream.c parser_h264.c spawn.c
 
-SRCS += http.c htmlui.c
+SRCS += http.c
 
 SRCS += htsp.c rpc.c
 
@@ -33,7 +33,9 @@ SRCS += FFdecsa.c
 #
 
 VPATH  += ajaxui
-SRCS   += ajaxui.c ajaxui_channels.c ajaxui_config.c
+SRCS   += ajaxui.c ajaxui_channels.c \
+         ajaxui_config.c ajaxui_config_channels.c ajaxui_config_dvb.c \
+         ajaxui_config_transport.c
 
 JSSRCS += tvheadend.js
 
@@ -41,7 +43,7 @@ CSSSRCS += ajaxui.css
 
 VPATH  += ajaxui/images
 GIFSRCS+= sbbody_l.gif sbbody_r.gif sbhead_l.gif sbhead_r.gif
-
+PNGSRCS+= mapped.png unmapped.png
 
 VPATH  += ajaxui/prototype
 JSSRCS += prototype.js
index d1afd5d5c4a86261987217d3fcc6bd7b2375480f..3b26e4275d2e436f2665ec55a9c870b8211908db 100644 (file)
@@ -44,6 +44,9 @@
 #include "obj/sbhead_l.gifh"
 #include "obj/sbhead_r.gifh"
 
+#include "obj/mapped.pngh"
+#include "obj/unmapped.pngh"
+
 
 
 const char *ajax_tabnames[] = {
@@ -194,6 +197,19 @@ ajax_menu_bar_from_array(tcp_queue_t *tq, const char *name,
 }
 
 
+/**
+ *
+ */
+void
+ajax_a_jsfunc(tcp_queue_t *tq, const char *innerhtml, const char *func,
+             const char *trailer)
+{
+  tcp_qprintf(tq, "<a href=\"javascript:void(0)\" "
+             "onClick=\"javascript:%s\">%s</a>%s\r\n",
+             func, innerhtml, trailer);
+}
+
+
 /*
  * Titlebar AJAX page
  */
@@ -252,9 +268,13 @@ ajax_page_root(http_connection_t *hc, const char *remain, void *opaque)
   tcp_init_queue(&tq, -1);
 
   tcp_qprintf(&tq, 
+             "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\r\n"
+             "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
+             /*
              "<!DOCTYPE html "
              "PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\r\n"
              "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+             */
              "\r\n"
              "<html xmlns=\"http://www.w3.org/1999/xhtml\" "
              "xml:lang=\"en\" lang=\"en\">"
@@ -413,7 +433,13 @@ ajaxui_start(void)
   http_resource_add("/sidebox/sbhead-r.gif", embedded_sbhead_r,
                    sizeof(embedded_sbhead_r), "image/gif", NULL);
 
+  http_resource_add("/gfx/unmapped.png", embedded_unmapped,
+                   sizeof(embedded_unmapped), "image/png", NULL);
+
+  http_resource_add("/gfx/mapped.png", embedded_mapped,
+                   sizeof(embedded_mapped), "image/png", NULL);
+
   ajax_channels_init();
   ajax_config_init();
-
+  ajax_config_transport_init();
 }
index eee3df9b43e7013c9f62b1771537bae7379f9b37..1755a466c5360c9a7878457dfeb01a437feff85a 100644 (file)
@@ -20,6 +20,19 @@ img { border: 0; }
    cursor:move;
  }
  
+
+
+.normallist {
+   margin:0;
+   margin-top:10px;
+   padding:0;
+   list-style-type: none;
+ }
+.normallist li {
+   margin:0;
+   padding:0px;
+ }
 /**
  * Input classes
  */
@@ -29,14 +42,52 @@ img { border: 0; }
   background: #fff
 }
 
+.nicebox {
+  margin: 1px;
+  padding: 0px;
+  border: 0px;
+  width: 11px;
+  height: 11px;
+}
+
+
 /**
  * Misc classes
  */
 
+.infoprefix {
+       float: left;
+       width: 50px;
+       text-align: right;
+}
+
+.infoprefixwide {
+       padding-right: 10px;
+       float: left;
+       width: 100px;
+       text-align: right;
+}
+
+.infoprefixwidefat {
+       padding-right: 10px;
+       float: left;
+       width: 130px;
+       text-align: right;
+       margin-top: 4px;
+}
+
+.pidheader {
+  float: left;
+  text-decoration: underline;
+}
+
+
+
 .chgroupaction {
   text-align: right;
 }
 
+
 /**
  *  Box with round edges
  */
index 1a0747615d45ef3812a4b5ed4aedd025e798b18d..8e94c4163a9fac147fcaf5ba999d0fbb030d2976 100644 (file)
@@ -50,7 +50,20 @@ void ajax_config_init(void);
 void ajax_menu_bar_from_array(tcp_queue_t *tq, const char *name, 
                              const char **vec, int num, int cur);
 
+void ajax_a_jsfunc(tcp_queue_t *tq, const char *innerhtml, const char *func,
+                  const char *trailer);
+
 int ajax_channelgroup_tab(http_connection_t *hc);
 int ajax_config_tab(http_connection_t *hc);
 
+int ajax_config_channels_tab(http_connection_t *hc);
+void ajax_config_channels_init(void);
+
+int ajax_config_dvb_tab(http_connection_t *hc);
+void ajax_config_dvb_init(void);
+void ajax_config_transport_init(void);
+
+int ajax_transport_build_list(tcp_queue_t *tq,
+                             struct th_transport_list *tlist);
+
 #endif /* AJAXUI_H_ */
index b0bab8f71137ab92af90c25c85b5cb0495f7a021..5806ad2776067afb35ef9c50b6a45319f79cfa5a 100644 (file)
 
 
 #define AJAX_CONFIG_TAB_CHANNELS 0
-#define AJAX_CONFIG_TAB_ADAPTERS 1
+#define AJAX_CONFIG_TAB_DVB      1
 #define AJAX_CONFIG_TABS         2
 
 const char *ajax_config_tabnames[] = {
   [AJAX_CONFIG_TAB_CHANNELS]      = "Channels & Groups",
-  [AJAX_CONFIG_TAB_ADAPTERS]      = "DVB adapters",
+  [AJAX_CONFIG_TAB_DVB]           = "DVB adapters",
 };
 
 
@@ -58,196 +58,6 @@ ajax_config_menu(http_connection_t *hc, const char *remain, void *opaque)
   return 0;
 }
 
-/**
- * Render a channel group widget
- */
-static void
-ajax_chgroup_build(tcp_queue_t *tq, th_channel_group_t *tcg)
-{
-  tcp_qprintf(tq, "<li id=\"chgrp_%d\">", tcg->tcg_tag);
-  
-  ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
-  
-  tcp_qprintf(tq,
-             "<div %s>%s</div>",
-             tcg == defgroup ? "" : "style=\"float: left\" ",
-             tcg->tcg_name);
-
-  if(tcg != defgroup) {
-    tcp_qprintf(tq,
-               "<div class=\"chgroupaction\">"
-               "<a href=\"javascript:void(0)\" "
-               "onClick=\"dellistentry('/ajax/chgroup_del','%d', '%s');\""
-               ">Delete</a></div>",
-               tcg->tcg_tag, tcg->tcg_name);
-  }
-  
-
-  ajax_box_end(tq, AJAX_BOX_BORDER);
-  tcp_qprintf(tq, "</li>");
-}
-
-/**
- * Update order of channel groups
- */
-static int
-ajax_chgroup_updateorder(http_connection_t *hc, const char *remain,
-                        void *opaque)
-{
-  th_channel_group_t *tcg;
-  tcp_queue_t tq;
-  http_arg_t *ra;
-
-  tcp_init_queue(&tq, -1);
-
-  TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
-    if(strcmp(ra->key, "channelgrouplist[]") ||
-       (tcg = channel_group_by_tag(atoi(ra->val))) == NULL)
-      continue;
-    
-    TAILQ_REMOVE(&all_channel_groups, tcg, tcg_global_link);
-    TAILQ_INSERT_TAIL(&all_channel_groups, tcg, tcg_global_link);
-  }
-
-  tcp_qprintf(&tq, "<span id=\"updatedok\">Updated on server</span>");
-  ajax_js(&tq, "Effect.Fade('updatedok')");
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
-  return 0;
-}
-
-
-
-/**
- * Add a new channel group
- */
-static int
-ajax_chgroup_add(http_connection_t *hc, const char *remain, void *opaque)
-{
-  th_channel_group_t *tcg;
-  tcp_queue_t tq;
-  const char *name;
-  
-  tcp_init_queue(&tq, -1);
-
-  if((name = http_arg_get(&hc->hc_req_args, "name")) != NULL) {
-
-    TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link)
-      if(!strcmp(name, tcg->tcg_name))
-       break;
-
-    if(tcg == NULL) {
-      tcg = channel_group_find(name, 1);
-
-      ajax_chgroup_build(&tq, tcg);
-
-      /* We must recreate the Sortable object */
-
-      ajax_js(&tq, "Sortable.destroy(\"channelgrouplist\")");
-
-      ajax_js(&tq, "Sortable.create(\"channelgrouplist\", "
-             "{onUpdate:function(){updatelistonserver("
-             "'channelgrouplist', "
-             "'/ajax/chgroup_updateorder', "
-             "'list-info'"
-             ")}});");
-    }
-  }
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
-  return 0;
-}
-
-
-
-/**
- * Delete a channel group
- */
-static int
-ajax_chgroup_del(http_connection_t *hc, const char *remain, void *opaque)
-{
-  th_channel_group_t *tcg;
-  tcp_queue_t tq;
-  const char *id;
-  
-  if((id = http_arg_get(&hc->hc_req_args, "id")) == NULL)
-    return HTTP_STATUS_BAD_REQUEST;
-
-  if((tcg = channel_group_by_tag(atoi(id))) == NULL)
-    return HTTP_STATUS_BAD_REQUEST;
-
-  tcp_init_queue(&tq, -1);
-  tcp_qprintf(&tq, "$('chgrp_%d').remove();", tcg->tcg_tag);
-  http_output_queue(hc, &tq, "text/javascript; charset=UTF-8", 0);
-
-  channel_group_destroy(tcg);
-
-  return 0;
-}
-
-
-
-/**
- * Channel group & channel configuration
- */
-static int
-ajax_channel_config_tab(http_connection_t *hc)
-{
-  tcp_queue_t tq;
-  th_channel_group_t *tcg;
-
-  tcp_init_queue(&tq, -1);
-  
-  tcp_qprintf(&tq, "<div style=\"float: left; width: 400px\">");
-
-  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, "channelgroups",
-                NULL, "Channel groups");
-
-  tcp_qprintf(&tq, "<div style=\"height:15px; text-align:center\" "
-             "id=\"list-info\"></div>");
-   
-  tcp_qprintf(&tq, "<ul id=\"channelgrouplist\" class=\"draglist\">");
-
-  TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link) {
-    if(tcg->tcg_hidden)
-      continue;
-    ajax_chgroup_build(&tq, tcg);
-  }
-
-  tcp_qprintf(&tq, "</ul>");
-  ajax_js(&tq, "Sortable.create(\"channelgrouplist\", "
-         "{onUpdate:function(){updatelistonserver("
-         "'channelgrouplist', "
-         "'/ajax/chgroup_updateorder', "
-         "'list-info'"
-         ")}});");
-
-  /**
-   * Add new group
-   */
-  ajax_box_begin(&tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
-
-  tcp_qprintf(&tq,
-             "<div style=\"height: 20px\">"
-             "<div style=\"float: left\">"
-             "<input class=\"textinput\" type=\"text\" id=\"newchgrp\">"
-             "</div>"
-             "<div style=\"padding-top:2px\" class=\"chgroupaction\">"
-             "<a href=\"javascript:void(0)\" "
-             "onClick=\"javascript:addlistentry_by_widget("
-             "'channelgrouplist', 'chgroup_add', 'newchgrp');\">"
-             "Add</a></div>"
-             "</div>");
-    
-  ajax_box_end(&tq, AJAX_BOX_BORDER);
-    
-  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
-  tcp_qprintf(&tq, "</div>");
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
-  return 0;
-}
-
-
-
 /*
  * Tab AJAX page
  *
@@ -265,7 +75,9 @@ ajax_config_dispatch(http_connection_t *hc, const char *remain, void *opaque)
 
   switch(tab) {
   case AJAX_CONFIG_TAB_CHANNELS:
-    return ajax_channel_config_tab(hc);
+    return ajax_config_channels_tab(hc);
+  case AJAX_CONFIG_TAB_DVB:
+    return ajax_config_dvb_tab(hc);
 
   default:
     return HTTP_STATUS_NOT_FOUND;
@@ -310,9 +122,9 @@ ajax_config_tab(http_connection_t *hc)
 void
 ajax_config_init(void)
 {
-  http_path_add("/ajax/chgroup_add"        , NULL, ajax_chgroup_add);
-  http_path_add("/ajax/chgroup_del"        , NULL, ajax_chgroup_del);
-  http_path_add("/ajax/chgroup_updateorder", NULL, ajax_chgroup_updateorder);
   http_path_add("/ajax/configmenu",          NULL, ajax_config_menu);
   http_path_add("/ajax/configtab",           NULL, ajax_config_dispatch);
+
+  ajax_config_channels_init();
+  ajax_config_dvb_init();
 }
diff --git a/ajaxui/ajaxui_config_channels.c b/ajaxui/ajaxui_config_channels.c
new file mode 100644 (file)
index 0000000..0c2a658
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ *  tvheadend, AJAX / HTML user interface
+ *  Copyright (C) 2008 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tvhead.h"
+#include "http.h"
+#include "ajaxui.h"
+#include "channels.h"
+
+
+
+/**
+ * Render a channel group widget
+ */
+static void
+ajax_chgroup_build(tcp_queue_t *tq, th_channel_group_t *tcg)
+{
+  tcp_qprintf(tq, "<li id=\"chgrp_%d\">", tcg->tcg_tag);
+  
+  ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
+  
+  tcp_qprintf(tq,
+             "<div %s>%s</div>",
+             tcg == defgroup ? "" : "style=\"float: left\" ",
+             tcg->tcg_name);
+
+  if(tcg != defgroup) {
+    tcp_qprintf(tq,
+               "<div class=\"chgroupaction\">"
+               "<a href=\"javascript:void(0)\" "
+               "onClick=\"dellistentry('/ajax/chgroup_del','%d', '%s');\""
+               ">Delete</a></div>",
+               tcg->tcg_tag, tcg->tcg_name);
+  }
+  
+
+  ajax_box_end(tq, AJAX_BOX_BORDER);
+  tcp_qprintf(tq, "</li>");
+}
+
+/**
+ * Update order of channel groups
+ */
+static int
+ajax_chgroup_updateorder(http_connection_t *hc, const char *remain,
+                        void *opaque)
+{
+  th_channel_group_t *tcg;
+  tcp_queue_t tq;
+  http_arg_t *ra;
+
+  tcp_init_queue(&tq, -1);
+
+  TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
+    if(strcmp(ra->key, "channelgrouplist[]") ||
+       (tcg = channel_group_by_tag(atoi(ra->val))) == NULL)
+      continue;
+    
+    TAILQ_REMOVE(&all_channel_groups, tcg, tcg_global_link);
+    TAILQ_INSERT_TAIL(&all_channel_groups, tcg, tcg_global_link);
+  }
+
+  tcp_qprintf(&tq, "<span id=\"updatedok\">Updated on server</span>");
+  ajax_js(&tq, "Effect.Fade('updatedok')");
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+
+
+/**
+ * Add a new channel group
+ */
+static int
+ajax_chgroup_add(http_connection_t *hc, const char *remain, void *opaque)
+{
+  th_channel_group_t *tcg;
+  tcp_queue_t tq;
+  const char *name;
+  
+  tcp_init_queue(&tq, -1);
+
+  if((name = http_arg_get(&hc->hc_req_args, "name")) != NULL) {
+
+    TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link)
+      if(!strcmp(name, tcg->tcg_name))
+       break;
+
+    if(tcg == NULL) {
+      tcg = channel_group_find(name, 1);
+
+      ajax_chgroup_build(&tq, tcg);
+
+      /* We must recreate the Sortable object */
+
+      ajax_js(&tq, "Sortable.destroy(\"channelgrouplist\")");
+
+      ajax_js(&tq, "Sortable.create(\"channelgrouplist\", "
+             "{onUpdate:function(){updatelistonserver("
+             "'channelgrouplist', "
+             "'/ajax/chgroup_updateorder', "
+             "'list-info'"
+             ")}});");
+    }
+  }
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+
+
+/**
+ * Delete a channel group
+ */
+static int
+ajax_chgroup_del(http_connection_t *hc, const char *remain, void *opaque)
+{
+  th_channel_group_t *tcg;
+  tcp_queue_t tq;
+  const char *id;
+  
+  if((id = http_arg_get(&hc->hc_req_args, "id")) == NULL)
+    return HTTP_STATUS_BAD_REQUEST;
+
+  if((tcg = channel_group_by_tag(atoi(id))) == NULL)
+    return HTTP_STATUS_BAD_REQUEST;
+
+  tcp_init_queue(&tq, -1);
+  tcp_qprintf(&tq, "$('chgrp_%d').remove();", tcg->tcg_tag);
+  http_output_queue(hc, &tq, "text/javascript; charset=UTF-8", 0);
+
+  channel_group_destroy(tcg);
+
+  return 0;
+}
+
+
+
+/**
+ * Channel group & channel configuration
+ */
+int
+ajax_config_channels_tab(http_connection_t *hc)
+{
+  tcp_queue_t tq;
+  th_channel_group_t *tcg;
+
+  tcp_init_queue(&tq, -1);
+  
+  tcp_qprintf(&tq, "<div style=\"float: left; width: 400px\">");
+
+  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, "channelgroups",
+                NULL, "Channel groups");
+
+  tcp_qprintf(&tq, "<div style=\"height:15px; text-align:center\" "
+             "id=\"list-info\"></div>");
+   
+  tcp_qprintf(&tq, "<ul id=\"channelgrouplist\" class=\"draglist\">");
+
+  TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link) {
+    if(tcg->tcg_hidden)
+      continue;
+    ajax_chgroup_build(&tq, tcg);
+  }
+
+  tcp_qprintf(&tq, "</ul>");
+  ajax_js(&tq, "Sortable.create(\"channelgrouplist\", "
+         "{onUpdate:function(){updatelistonserver("
+         "'channelgrouplist', "
+         "'/ajax/chgroup_updateorder', "
+         "'list-info'"
+         ")}});");
+
+  /**
+   * Add new group
+   */
+  ajax_box_begin(&tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
+
+  tcp_qprintf(&tq,
+             "<div style=\"height: 20px\">"
+             "<div style=\"float: left\">"
+             "<input class=\"textinput\" type=\"text\" id=\"newchgrp\">"
+             "</div>"
+             "<div style=\"padding-top:2px\" class=\"chgroupaction\">"
+             "<a href=\"javascript:void(0)\" "
+             "onClick=\"javascript:addlistentry_by_widget("
+             "'channelgrouplist', 'chgroup_add', 'newchgrp');\">"
+             "Add</a></div>"
+             "</div>");
+    
+  ajax_box_end(&tq, AJAX_BOX_BORDER);
+    
+  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
+  tcp_qprintf(&tq, "</div>");
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+/**
+ *
+ */
+void
+ajax_config_channels_init(void)
+{
+  http_path_add("/ajax/chgroup_add"        , NULL, ajax_chgroup_add);
+  http_path_add("/ajax/chgroup_del"        , NULL, ajax_chgroup_del);
+  http_path_add("/ajax/chgroup_updateorder", NULL, ajax_chgroup_updateorder);
+
+  
+
+}
diff --git a/ajaxui/ajaxui_config_dvb.c b/ajaxui/ajaxui_config_dvb.c
new file mode 100644 (file)
index 0000000..a601640
--- /dev/null
@@ -0,0 +1,662 @@
+/*
+ *  tvheadend, AJAX / HTML user interface
+ *  Copyright (C) 2008 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 <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+
+#include <pthread.h>
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/dvb/frontend.h>
+
+#include "tvhead.h"
+#include "http.h"
+#include "ajaxui.h"
+#include "channels.h"
+#include "dvb.h"
+#include "dvb_support.h"
+#include "dvb_muxconfig.h"
+#include "strtab.h"
+#include "psi.h"
+#include "transports.h"
+
+
+static struct strtab adapterstatus[] = {
+  { "Unconfigured",                   TDA_STATE_UNCONFIGURED },
+  { "Running",                        TDA_STATE_RUNNING },
+  { "Zombie",                         TDA_STATE_ZOMBIE },
+};
+
+static void
+add_option(tcp_queue_t *tq, int bol, const char *name)
+{
+  if(bol)
+    tcp_qprintf(tq, "<option>%s</option>", name);
+}
+
+
+static void
+tdmi_displayname(th_dvb_mux_instance_t *tdmi, char *buf, size_t len)
+{
+  int f = tdmi->tdmi_fe_params->frequency;
+
+  if(tdmi->tdmi_adapter->tda_fe_info->type == FE_QPSK) {
+    snprintf(buf, len, "%d kHz %s", f,
+            dvb_polarisation_to_str(tdmi->tdmi_polarisation));
+  } else {
+    snprintf(buf, len, "%d kHz", f / 1000);
+  }
+}
+
+/*
+ * Adapter summary
+ */
+static int
+ajax_adaptersummary(http_connection_t *hc, const char *remain, void *opaque)
+{
+  tcp_queue_t tq;
+  th_dvb_adapter_t *tda;
+  char dispname[20];
+
+  if(remain == NULL || (tda = dvb_adapter_find_by_identifier(remain)) == NULL)
+    return HTTP_STATUS_NOT_FOUND;
+
+  tcp_init_queue(&tq, -1);
+
+  snprintf(dispname, sizeof(dispname), "%s", tda->tda_displayname);
+  strcpy(dispname + sizeof(dispname) - 4, "...");
+
+  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, NULL, NULL, dispname);
+
+  tcp_qprintf(&tq, "<div class=\"infoprefix\">Status:</div>"
+             "<div>%s</div>",
+             val2str(tda->tda_state, adapterstatus) ?: "invalid");
+  tcp_qprintf(&tq, "<div class=\"infoprefix\">Type:</div>"
+             "<div>%s</div>", 
+             dvb_adaptertype_to_str(tda->tda_fe_info->type));
+  tcp_qprintf(&tq, "<div class=\"infoprefix\">Tuner:</div>"
+             "<div>...</div>");
+  tcp_qprintf(&tq, "<div style=\"text-align: center\">"
+             "<a href=\"javascript:void(0);\" "
+             "onClick=\"new Ajax.Updater('dvbadaptereditor', "
+             "'/ajax/dvbadaptereditor/%s', "
+             "{method: 'get', evalScripts: true})\""
+             "\">Edit</a></div>", tda->tda_identifier);
+  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
+
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+
+/**
+ * DVB configuration
+ */
+int
+ajax_config_dvb_tab(http_connection_t *hc)
+{
+  tcp_queue_t tq;
+  th_dvb_adapter_t *tda;
+
+  tcp_init_queue(&tq, -1);
+
+  tcp_qprintf(&tq, "<div style=\"overflow: auto; width: 100%\">");
+
+  LIST_FOREACH(tda, &dvb_adapters, tda_global_link) {
+
+    tcp_qprintf(&tq, "<div id=\"summary_%s\" "
+               "style=\"float:left; width: 250px\"></div>",
+               tda->tda_identifier);
+
+    ajax_js(&tq, "new Ajax.Updater('summary_%s', "
+           "'/ajax/dvbadaptersummary/%s', {method: 'get'})",
+           tda->tda_identifier, tda->tda_identifier);
+
+  }
+  tcp_qprintf(&tq, "</div>");
+  tcp_qprintf(&tq, "<div id=\"dvbadaptereditor\"></div>");
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+/**
+ * Generate the 'add new...' mux link
+ *
+ * if result is set we add a fade out of a result from a previous op
+ */
+static void
+dvb_make_add_link(tcp_queue_t *tq, th_dvb_adapter_t *tda, const char *result)
+{
+  tcp_qprintf(tq,
+             "<p><a href=\"javascript:void(0);\" "
+             "onClick=\"new Ajax.Updater('addmux', "
+             "'/ajax/dvbadapteraddmux/%s', {method: 'get'})\""
+             ">Add new...</a></p>", tda->tda_identifier);
+
+  if(result) {
+    tcp_qprintf(tq, "<div id=\"result\">%s</div>", result);
+    ajax_js(tq, "Effect.Fade('result')");
+  }
+}
+
+
+/**
+ * DVB adapter editor pane
+ */
+static int
+ajax_adaptereditor(http_connection_t *hc, const char *remain, void *opaque)
+{
+  tcp_queue_t tq;
+  th_dvb_adapter_t *tda;
+  float a, b, c;
+
+  if(remain == NULL || (tda = dvb_adapter_find_by_identifier(remain)) == NULL)
+    return HTTP_STATUS_NOT_FOUND;
+
+  tcp_init_queue(&tq, -1);
+
+  ajax_box_begin(&tq, AJAX_BOX_FILLED, NULL, NULL, NULL);
+
+  tcp_qprintf(&tq, 
+             "<div style=\"text-align: center; font-weight: bold\">%s</div>",
+             tda->tda_displayname);
+  
+  ajax_box_end(&tq, AJAX_BOX_FILLED);
+
+  /* Type */
+
+  tcp_qprintf(&tq, "<div class=\"infoprefixwide\">Model:</div>"
+             "<div>%s (%s)</div>", 
+             tda->tda_fe_info->name,
+             dvb_adaptertype_to_str(tda->tda_fe_info->type));
+  
+  switch(tda->tda_fe_info->type) {
+  case FE_QPSK:
+    a = tda->tda_fe_info->frequency_min;
+    b = tda->tda_fe_info->frequency_max;
+    c = tda->tda_fe_info->frequency_stepsize;
+    break;
+
+  default:
+    a = tda->tda_fe_info->frequency_min / 1000.0f;
+    b = tda->tda_fe_info->frequency_max / 1000.0f;
+    c = tda->tda_fe_info->frequency_stepsize / 1000.0f;
+    break;
+  }
+
+  tcp_qprintf(&tq, "<div class=\"infoprefixwide\">Freq. Range:</div>"
+             "<div>%.2f - %.2f kHz, in steps of %.2f kHz</div>",
+             a, b, c);
+
+
+  if(tda->tda_fe_info->symbol_rate_min) {
+    tcp_qprintf(&tq, "<div class=\"infoprefixwide\">Symbolrate:</div>"
+               "<div>%d - %d BAUD</div>",
+               tda->tda_fe_info->symbol_rate_min,
+               tda->tda_fe_info->symbol_rate_max);
+  }
+
+  /* Capabilities */
+
+
+  //  tcp_qprintf(&tq, "<div class=\"infoprefixwide\">Capabilities:</div>");
+
+  tcp_qprintf(&tq, "<div style=\"float: left; width:45%\">");
+  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, NULL, NULL, "Multiplexes");
+
+  /* List of muxes */
+
+  tcp_qprintf(&tq, "<div style=\"overflow: auto; width: 100%\">");
+
+  tcp_qprintf(&tq, "<div style=\"float: left; width: 20%\">"
+             "Freq.</div>");
+  tcp_qprintf(&tq, "<div style=\"float: left; width: 25%\">%s</div>",
+             "Status");
+  tcp_qprintf(&tq, "<div style=\"float: left; width: 15%\">%s</div>",
+             "State");
+  tcp_qprintf(&tq, "<div style=\"float: left; width: 25%\">%s</div>",
+             "Name");
+  tcp_qprintf(&tq, "<div style=\"float: left; width: 15%\">%s</div>",
+             "Services");
+  tcp_qprintf(&tq, "</div><hr>");
+
+
+  tcp_qprintf(&tq, "<div id=\"dvbmuxlist%s\" class=\"normallist\">",
+             tda->tda_identifier);
+  tcp_qprintf(&tq, "</div>");
+  
+  
+
+  ajax_js(&tq, 
+         "new Ajax.PeriodicalUpdater('dvbmuxlist%s', "
+         "'/ajax/dvbadaptermuxlist/%s', {method: 'get', frequency: 5}) ",
+         tda->tda_identifier, tda->tda_identifier);
+
+  tcp_qprintf(&tq, "<hr><div id=\"addmux\">");
+  dvb_make_add_link(&tq, tda, NULL);
+  tcp_qprintf(&tq, "</div>");
+
+  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
+  tcp_qprintf(&tq, "</div>");
+
+  /* Div for displaying services */
+
+  tcp_qprintf(&tq, "<div id=\"servicepane\" "
+             "style=\"float: left; width:55%\">");
+  tcp_qprintf(&tq, "</div>");
+
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+
+/**
+ * DVB adapter add mux
+ */
+static int
+ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
+{
+  tcp_queue_t tq;
+  th_dvb_adapter_t *tda;
+  int caps;
+  int fetype;
+  char params[400];
+
+  if(remain == NULL || (tda = dvb_adapter_find_by_identifier(remain)) == NULL)
+    return HTTP_STATUS_NOT_FOUND;
+
+  caps   = tda->tda_fe_info->caps;
+  fetype = tda->tda_fe_info->type;
+
+  tcp_init_queue(&tq, -1);
+
+  tcp_qprintf(&tq, "<div style=\"text-align: center; font-weight: bold\">"
+             "Add new %s mux</div>",
+             dvb_adaptertype_to_str(tda->tda_fe_info->type));
+
+  tcp_qprintf(&tq,
+             "<div class=\"infoprefixwidefat\">Frequency (%s):</div>"
+             "<div>"
+             "<input class=\"textinput\" type=\"text\" id=\"freq\">"
+             "</div>",
+             fetype == FE_QPSK ? "kHz" : "Hz");
+
+  snprintf(params, sizeof(params), 
+          "freq: $F('freq')");
+  
+
+  /* Symbolrate */
+
+  if(fetype == FE_QAM || fetype == FE_QPSK) {
+    tcp_qprintf(&tq,
+               "<div class=\"infoprefixwidefat\">Symbolrate:</div>"
+               "<div>"
+               "<input class=\"textinput\" type=\"text\" id=\"symrate\">"
+               "</div>");
+
+    snprintf(params + strlen(params), sizeof(params) - strlen(params), 
+            ", symrate: $F('symrate')");
+  }
+
+  /* Bandwidth */
+
+  if(fetype == FE_OFDM) {
+    tcp_qprintf(&tq,
+               "<div class=\"infoprefixwidefat\">Bandwidth:</div>"
+               "<div><select id=\"bw\" class=\"textinput\">");
+
+    add_option(&tq, caps & FE_CAN_BANDWIDTH_AUTO, "AUTO");
+    add_option(&tq, 1                           , "8MHz");
+    add_option(&tq, 1                           , "7MHz");
+    add_option(&tq, 1                           , "6MHz");
+    tcp_qprintf(&tq, "</select></div>");
+
+    snprintf(params + strlen(params), sizeof(params) - strlen(params), 
+            ", bw: $F('bw')");
+  }
+
+
+  /* Constellation */
+
+  if(fetype == FE_QAM || fetype == FE_OFDM) {
+    tcp_qprintf(&tq,
+               "<div class=\"infoprefixwidefat\">Constellation:</div>"
+               "<div><select id=\"const\" class=\"textinput\">");
+
+    add_option(&tq, caps & FE_CAN_QAM_AUTO,  "AUTO");
+    add_option(&tq, caps & FE_CAN_QPSK,      "QPSK");
+    add_option(&tq, caps & FE_CAN_QAM_16,    "QAM16");
+    add_option(&tq, caps & FE_CAN_QAM_32,    "QAM32");
+    add_option(&tq, caps & FE_CAN_QAM_64,    "QAM64");
+    add_option(&tq, caps & FE_CAN_QAM_128,   "QAM128");
+    add_option(&tq, caps & FE_CAN_QAM_256,   "QAM256");
+
+    tcp_qprintf(&tq, "</select></div>");
+
+    snprintf(params + strlen(params), sizeof(params) - strlen(params), 
+            ", const: $F('const')");
+  }
+
+
+  /* FEC */
+
+  if(fetype == FE_QAM || fetype == FE_QPSK) {
+    tcp_qprintf(&tq,
+               "<div class=\"infoprefixwidefat\">FEC:</div>"
+               "<div><select id=\"fec\" class=\"textinput\">");
+
+    add_option(&tq, caps & FE_CAN_FEC_AUTO,  "AUTO");
+    add_option(&tq, caps & FE_CAN_FEC_1_2,   "1/2");
+    add_option(&tq, caps & FE_CAN_FEC_2_3,   "2/3");
+    add_option(&tq, caps & FE_CAN_FEC_3_4,   "3/4");
+    add_option(&tq, caps & FE_CAN_FEC_4_5,   "4/5");
+    add_option(&tq, caps & FE_CAN_FEC_5_6,   "5/6");
+    add_option(&tq, caps & FE_CAN_FEC_6_7,   "6/7");
+    add_option(&tq, caps & FE_CAN_FEC_7_8,   "7/8");
+    add_option(&tq, caps & FE_CAN_FEC_8_9,   "8/9");
+    tcp_qprintf(&tq, "</select></div>");
+
+    snprintf(params + strlen(params), sizeof(params) - strlen(params), 
+            ", fec: $F('fec')");
+  }
+
+
+  if(fetype == FE_OFDM) {
+    tcp_qprintf(&tq,
+               "<div class=\"infoprefixwidefat\">Transmission mode:</div>"
+               "<div><select id=\"tmode\" class=\"textinput\">");
+    
+    add_option(&tq, caps & FE_CAN_TRANSMISSION_MODE_AUTO, "AUTO");
+    add_option(&tq, 1                                   , "2k");
+    add_option(&tq, 1                                   , "8k");
+    tcp_qprintf(&tq, "</select></div>");
+
+    snprintf(params + strlen(params), sizeof(params) - strlen(params), 
+            ", tmode: $F('tmode')");
+
+    tcp_qprintf(&tq,
+               "<div class=\"infoprefixwidefat\">Guard interval:</div>"
+               "<div><select id=\"guard\" class=\"textinput\">");
+    
+    add_option(&tq, caps & FE_CAN_GUARD_INTERVAL_AUTO, "AUTO");
+    add_option(&tq, 1                                , "1/32");
+    add_option(&tq, 1                                , "1/16");
+    add_option(&tq, 1                                , "1/8");
+    add_option(&tq, 1                                , "1/4");
+    tcp_qprintf(&tq, "</select></div>");
+
+    snprintf(params + strlen(params), sizeof(params) - strlen(params), 
+            ", guard: $F('guard')");
+
+
+
+    tcp_qprintf(&tq,
+               "<div class=\"infoprefixwidefat\">Hierarchy:</div>"
+               "<div><select id=\"hier\" class=\"textinput\">");
+    
+    add_option(&tq, caps & FE_CAN_HIERARCHY_AUTO, "AUTO");
+    add_option(&tq, 1                           , "1");
+    add_option(&tq, 1                           , "2");
+    add_option(&tq, 1                           , "4");
+    add_option(&tq, 1                           , "NONE");
+    tcp_qprintf(&tq, "</select></div>");
+
+
+    snprintf(params + strlen(params), sizeof(params) - strlen(params), 
+            ", hier: $F('hier')");
+
+
+
+    tcp_qprintf(&tq,
+               "<div class=\"infoprefixwidefat\">FEC Hi:</div>"
+               "<div><select id=\"fechi\" class=\"textinput\">");
+    
+    add_option(&tq, caps & FE_CAN_FEC_AUTO,  "AUTO");
+    add_option(&tq, caps & FE_CAN_FEC_1_2,   "1/2");
+    add_option(&tq, caps & FE_CAN_FEC_2_3,   "2/3");
+    add_option(&tq, caps & FE_CAN_FEC_3_4,   "3/4");
+    add_option(&tq, caps & FE_CAN_FEC_4_5,   "4/5");
+    add_option(&tq, caps & FE_CAN_FEC_5_6,   "5/6");
+    add_option(&tq, caps & FE_CAN_FEC_6_7,   "6/7");
+    add_option(&tq, caps & FE_CAN_FEC_7_8,   "7/8");
+    add_option(&tq, caps & FE_CAN_FEC_8_9,   "8/9");
+    tcp_qprintf(&tq, "</select></div>");
+
+    snprintf(params + strlen(params), sizeof(params) - strlen(params), 
+            ", fechi: $F('fechi')");
+
+
+    tcp_qprintf(&tq,
+               "<div class=\"infoprefixwidefat\">FEC Low:</div>"
+               "<div><select id=\"feclo\" class=\"textinput\">");
+    
+    add_option(&tq, caps & FE_CAN_FEC_AUTO,  "AUTO");
+    add_option(&tq, caps & FE_CAN_FEC_1_2,   "1/2");
+    add_option(&tq, caps & FE_CAN_FEC_2_3,   "2/3");
+    add_option(&tq, caps & FE_CAN_FEC_3_4,   "3/4");
+    add_option(&tq, caps & FE_CAN_FEC_4_5,   "4/5");
+    add_option(&tq, caps & FE_CAN_FEC_5_6,   "5/6");
+    add_option(&tq, caps & FE_CAN_FEC_6_7,   "6/7");
+    add_option(&tq, caps & FE_CAN_FEC_7_8,   "7/8");
+    add_option(&tq, caps & FE_CAN_FEC_8_9,   "8/9");
+    tcp_qprintf(&tq, "</select></div>");
+
+    snprintf(params + strlen(params), sizeof(params) - strlen(params), 
+            ", feclo: $F('feclo')");
+  }
+
+  tcp_qprintf(&tq,
+             "<div style=\"text-align: center\">"
+             "<input type=\"button\" value=\"Create\" "
+             "onClick=\"new Ajax.Updater('addmux', "
+             "'/ajax/dvbadaptercreatemux/%s', "
+             "{evalScripts: true, parameters: {%s}})"
+             "\">"
+             "</div>", tda->tda_identifier, params);
+
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+/**
+ *
+ */
+static int
+ajax_adaptercreatemux_fail(http_connection_t *hc, th_dvb_adapter_t *tda, 
+                          const char *errmsg)
+{
+  tcp_queue_t tq;
+  tcp_init_queue(&tq, -1);
+  dvb_make_add_link(&tq, tda, errmsg);
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+/**
+ * DVB adapter create mux (come here on POST after addmux query)
+ */
+static int
+ajax_adaptercreatemux(http_connection_t *hc, const char *remain, void *opaque)
+{
+  tcp_queue_t tq;
+  th_dvb_adapter_t *tda;
+  const char *v;
+
+  if(remain == NULL || (tda = dvb_adapter_find_by_identifier(remain)) == NULL)
+    return HTTP_STATUS_NOT_FOUND;
+
+  v = dvb_mux_create_str(tda,
+                        http_arg_get(&hc->hc_req_args, "freq"),
+                        http_arg_get(&hc->hc_req_args, "symrate"),
+                        http_arg_get(&hc->hc_req_args, "const"),
+                        http_arg_get(&hc->hc_req_args, "fec"),
+                        http_arg_get(&hc->hc_req_args, "fechi"),
+                        http_arg_get(&hc->hc_req_args, "feclo"),
+                        http_arg_get(&hc->hc_req_args, "bw"),
+                        http_arg_get(&hc->hc_req_args, "tmode"),
+                        http_arg_get(&hc->hc_req_args, "guard"),
+                        http_arg_get(&hc->hc_req_args, "hier"),
+                        http_arg_get(&hc->hc_req_args, "pol"),
+                        http_arg_get(&hc->hc_req_args, "port"), 1);
+
+  if(v != NULL)
+    return ajax_adaptercreatemux_fail(hc, tda, v);
+
+  tcp_init_queue(&tq, -1);
+  dvb_make_add_link(&tq, tda, "Successfully created");
+
+  ajax_js(&tq, 
+         "new Ajax.Updater('dvbmuxlist%s', "
+         "'/ajax/dvbadaptermuxlist/%s', {method: 'get'}) ",
+         tda->tda_identifier, tda->tda_identifier);
+
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+
+/**
+ * Return a list of all muxes on the given adapter
+ */
+static int
+ajax_adaptermuxlist(http_connection_t *hc, const char *remain, void *opaque)
+{
+  th_dvb_mux_instance_t *tdmi;
+  tcp_queue_t tq;
+  th_dvb_adapter_t *tda;
+  char buf[50];
+  const char *txt;
+  int fetype, n;
+  th_transport_t *t;
+  int o = 1;
+
+  if(remain == NULL || (tda = dvb_adapter_find_by_identifier(remain)) == NULL)
+    return HTTP_STATUS_NOT_FOUND;
+
+  fetype = tda->tda_fe_info->type;
+
+  tcp_init_queue(&tq, -1);
+
+  if(LIST_FIRST(&tda->tda_muxes) == NULL) {
+    tcp_qprintf(&tq, "<div style=\"text-align: center\">"
+               "No muxes configured</div>");
+  } else LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
+
+    tcp_qprintf(&tq, "<div%s>", o ? " style=\"background: #fff\"" : "");
+    o = !o;
+
+    tdmi_displayname(tdmi, buf, sizeof(buf));
+
+    tcp_qprintf(&tq, "<div style=\"overflow: auto; width: 100%\">");
+    tcp_qprintf(&tq, "<div style=\"float: left; width: 20%\">"
+               "<a href=\"javascript:void(0);\" "
+               "onClick=\"new Ajax.Updater('servicepane', "
+               "'/ajax/dvbmuxeditor/%s', "
+               "{method: 'get', evalScripts: true})\""
+               ">%s</a></div>", tdmi->tdmi_identifier, buf);
+
+    tcp_qprintf(&tq, "<div style=\"float: left; width: 25%\">%s</div>",
+               dvb_mux_status(tdmi));
+
+    switch(tdmi->tdmi_state) {
+    case TDMI_IDLE:      txt = "Idle";      break;
+    case TDMI_IDLESCAN:  txt = "Scanning";  break;
+    case TDMI_RUNNING:   txt = "Running";   break;
+    default:             txt = "???";       break;
+    }
+
+    tcp_qprintf(&tq, "<div style=\"float: left; width: 15%\">%s</div>",
+               txt);
+
+    txt = tdmi->tdmi_network;
+    if(txt == NULL)
+      txt = "Unknown";
+
+    tcp_qprintf(&tq, "<div style=\"float: left; width: 25%\">%s</div>",
+               txt);
+
+    n = 0;
+    LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link)
+      n++;
+
+    tcp_qprintf(&tq, "<div style=\"float: left; width: 15%\">%d</div>", n);
+
+    tcp_qprintf(&tq, "</div></div>");
+  }
+
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+/**
+ * Display detailes about a mux
+ */
+static int
+ajax_dvbmuxeditor(http_connection_t *hc, const char *remain, void *opaque)
+{
+  th_dvb_mux_instance_t *tdmi;
+  tcp_queue_t tq;
+  char buf[1000];
+  th_transport_t *t;
+  struct th_transport_list head;
+
+  if(remain == NULL || (tdmi = dvb_mux_find_by_identifier(remain)) == NULL)
+    return HTTP_STATUS_NOT_FOUND;
+
+  tcp_init_queue(&tq, -1);
+
+  tdmi_displayname(tdmi, buf, sizeof(buf));
+
+  LIST_INIT(&head);
+
+  LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) {
+    if(transport_servicetype_txt(t) == NULL)
+      continue;
+    LIST_INSERT_HEAD(&head, t, tht_tmp_link);
+  }
+
+  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, NULL, NULL, buf);
+  ajax_transport_build_list(&tq, &head);
+  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
+
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  return 0;
+}
+
+
+/**
+ *
+ */
+void
+ajax_config_dvb_init(void)
+{
+  http_path_add("/ajax/dvbadaptermuxlist"   , NULL, ajax_adaptermuxlist);
+  http_path_add("/ajax/dvbadaptersummary"   , NULL, ajax_adaptersummary);
+  http_path_add("/ajax/dvbadaptereditor",     NULL, ajax_adaptereditor);
+  http_path_add("/ajax/dvbadapteraddmux",     NULL, ajax_adapteraddmux);
+  http_path_add("/ajax/dvbadaptercreatemux",  NULL, ajax_adaptercreatemux);
+  http_path_add("/ajax/dvbmuxeditor",         NULL, ajax_dvbmuxeditor);
+
+}
diff --git a/ajaxui/ajaxui_config_transport.c b/ajaxui/ajaxui_config_transport.c
new file mode 100644 (file)
index 0000000..32f7411
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ *  tvheadend, AJAX / HTML user interface
+ *  Copyright (C) 2008 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 <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+
+#include <pthread.h>
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/dvb/frontend.h>
+
+#include "tvhead.h"
+#include "http.h"
+#include "ajaxui.h"
+#include "channels.h"
+#include "strtab.h"
+#include "psi.h"
+#include "transports.h"
+
+
+/**
+ * 
+ */
+int
+ajax_transport_build_list(tcp_queue_t *tq, struct th_transport_list *tlist)
+{
+  char buf[1000];
+  th_transport_t *t;
+  const char *v;
+  int o = 1;
+  th_stream_t *st;
+  const char *extra;
+
+
+  tcp_qprintf(tq, "<script type=\"text/javascript\">\r\n"
+             "//<![CDATA[\r\n");
+  
+  /* Select all */
+  tcp_qprintf(tq, "select_all = function() {\r\n");
+  LIST_FOREACH(t, tlist, tht_tmp_link) {
+    tcp_qprintf(tq, 
+               "document.getElementById('sel_%s').checked = true;\r\n",
+               t->tht_identifier);
+  }
+  tcp_qprintf(tq, "}\r\n");
+
+  /* Select none */
+  tcp_qprintf(tq, "select_none = function() {\r\n");
+  LIST_FOREACH(t, tlist, tht_tmp_link) {
+    tcp_qprintf(tq, 
+               "document.getElementById('sel_%s').checked = false;\r\n",
+               t->tht_identifier);
+  }
+  tcp_qprintf(tq, "}\r\n");
+
+  /* Invert selection */
+  tcp_qprintf(tq, "select_invert = function() {\r\n");
+  LIST_FOREACH(t, tlist, tht_tmp_link) {
+    tcp_qprintf(tq, 
+               "document.getElementById('sel_%s').checked = !"
+               "document.getElementById('sel_%s').checked;\r\n",
+               t->tht_identifier, t->tht_identifier);
+  }
+  tcp_qprintf(tq, "}\r\n");
+
+  /* Select TV transports */
+  tcp_qprintf(tq, "select_tv = function() {\r\n");
+  LIST_FOREACH(t, tlist, tht_tmp_link) {
+    tcp_qprintf(tq, 
+               "document.getElementById('sel_%s').checked = %s;\r\n",
+               t->tht_identifier, 
+               transport_is_tv(t) ? "true" : "false");
+  }
+  tcp_qprintf(tq, "}\r\n");
+
+  /* Select unscrambled TV transports */
+  tcp_qprintf(tq, "select_tv_nocrypt = function() {\r\n");
+  LIST_FOREACH(t, tlist, tht_tmp_link) {
+    tcp_qprintf(tq, 
+               "document.getElementById('sel_%s').checked = %s;\r\n",
+               t->tht_identifier, 
+               transport_is_tv(t) && !t->tht_scrambled ? "true" : "false");
+  }
+  tcp_qprintf(tq, "}\r\n");
+
+  /* Perform the given op on all transprots */
+  tcp_qprintf(tq, "selected_do = function(op) {\r\n");
+  LIST_FOREACH(t, tlist, tht_tmp_link) {
+    tcp_qprintf(tq, 
+               "if(document.getElementById('sel_%s').checked) {\r\n"
+               "  new Ajax.Request('/ajax/transport_op/%s', "
+               "{parameters: {action: op}});\r\n}\r\n",
+               t->tht_identifier, t->tht_identifier);
+  }
+  tcp_qprintf(tq, "}\r\n");
+
+
+  tcp_qprintf(tq, 
+             "\r\n//]]>\r\n"
+             "</script>\r\n");
+
+  /* Top */
+
+  tcp_qprintf(tq, "<form id=\"transports\">");
+
+  tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
+
+  tcp_qprintf(tq, "<div style=\"float: left; width: 8%\">&nbsp;</div>");
+  tcp_qprintf(tq, "<div style=\"float: left; width: 8%\">SID</div>");
+
+  tcp_qprintf(tq, "<div style=\"float: left; width: 10%\">Crypto</div>");
+
+  tcp_qprintf(tq, "<div style=\"float: left; width: 12%\">Type</div>");
+
+  tcp_qprintf(tq, "<div style=\"float: left; width: 25%\">"
+             "Service</div>");
+
+  tcp_qprintf(tq, "<div style=\"float: left; width: 6%\">&nbsp;</div>");
+
+  tcp_qprintf(tq, "<div style=\"float: left; width: 25%\">"
+             "Channel</div>");
+
+  tcp_qprintf(tq, "<div style=\"float: left; width: 6%\">"
+             "Bah</div>");
+
+  tcp_qprintf(tq, "</div><hr>");
+
+
+  LIST_FOREACH(t, tlist, tht_tmp_link) {
+    tcp_qprintf(tq, "<div%s>", o ? " style=\"background: #fff\"" : "");
+    o = !o;
+
+    tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
+
+    tcp_qprintf(tq, "<div style=\"float: left; width: 8%\">"
+               "<a id=\"toggle_svcinfo%s\" href=\"javascript:void(0)\" "
+               "onClick=\"showhide('svcinfo%s')\" >"
+               "More</a></div>", t->tht_identifier, t->tht_identifier);
+
+    tcp_qprintf(tq, "<div style=\"float: left; width: 8%\">%d</div>",
+               t->tht_dvb_service_id);
+
+    tcp_qprintf(tq, "<div style=\"float: left; width: 10%\">%s</div>",
+               t->tht_scrambled ? "CSA" : "Free");
+
+    tcp_qprintf(tq, "<div style=\"float: left; width: 12%\">%s</div>",
+               transport_servicetype_txt(t));
+
+    tcp_qprintf(tq, "<div style=\"float: left; width: 25%\">%s</div>",
+               t->tht_servicename ?: "");
+
+    tcp_qprintf(tq, 
+               "<div style=\"float: left; width: 6%; text-align: center\">"
+               "<a href=\"javascript:void(0)\" "
+               "onClick=\"new "
+               "Ajax.Request('/ajax/transport_op/%s', "
+               "{parameters: {action: 'toggle'}})\">"
+               "<img id=\"map%s\" src=\"/gfx/unmapped.png\"></a></div>",
+               t->tht_identifier, t->tht_identifier);
+
+    tcp_qprintf(tq, "<div id=\"chname%s\" style=\"float: left; width: 25%\">",
+               t->tht_identifier);
+
+    if(t->tht_channel == NULL) {
+      /* Unmapped */
+
+      v = t->tht_channelname;
+
+      snprintf(buf, sizeof(buf), 
+              "tentative_chname('chname%s', "
+              "'/ajax/transport_rename_channel/%s', '%s')",
+              t->tht_identifier, t->tht_identifier, v);
+
+      ajax_a_jsfunc(tq, v, buf, "");
+    } else {
+      /* Mapped */
+      tcp_qprintf(tq, "%s", t->tht_channel->ch_name);
+    }
+    tcp_qprintf(tq, "</div>");
+
+
+
+    tcp_qprintf(tq, "<div style=\"float: left; width: 6%\">"
+               "<input id=\"sel_%s\" type=\"checkbox\" class=\"nicebox\">"
+               "</div>", t->tht_identifier);
+    
+    tcp_qprintf(tq, "</div>");
+
+    /* Extra info */
+    tcp_qprintf(tq, "<div name=\"id=\"svcinfo%s\" style=\"display: none\">",
+               t->tht_identifier);
+
+    tcp_qprintf(tq, "<p>");
+      
+    tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
+    tcp_qprintf(tq, "<div style=\"float: left; width: 8%\">"
+               "&nbsp;</div>");
+    tcp_qprintf(tq, "<div class=\"pidheader\" style=\"width: 8%\">"
+               "PID</div>");
+
+    tcp_qprintf(tq, "<div class=\"pidheader\" style=\"width: 22%\">"
+               "Payload</div>");
+
+    tcp_qprintf(tq, "<div class=\"pidheader\" style=\"width: 62%\">Details"
+               "</div>");
+    tcp_qprintf(tq, "</div>");
+
+
+    LIST_FOREACH(st, &t->tht_streams, st_link) {
+      tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
+      tcp_qprintf(tq, "<div style=\"float: left; width: 8%\">&nbsp;</div>");
+      tcp_qprintf(tq, "<div style=\"float: left; width: 8%\">%d</div>",
+                 st->st_pid);
+      tcp_qprintf(tq, "<div style=\"float: left; width: 22%\">%s</div>",
+                 htstvstreamtype2txt(st->st_type));
+
+      switch(st->st_type) {
+      case HTSTV_MPEG2AUDIO:
+      case HTSTV_AC3:
+       extra = st->st_lang;
+       break;
+      case HTSTV_CA:
+       extra = psi_caid2name(st->st_caid);
+       break;
+      default:
+       extra = NULL;
+       break;
+      }
+
+      if(extra != NULL)
+       tcp_qprintf(tq, "<div style=\"float: left; width: 62%\">%s</div>",
+                   extra);
+      
+      tcp_qprintf(tq, "</div>");
+    }
+    
+    tcp_qprintf(tq, "</p></div>");
+    tcp_qprintf(tq, "</div>\r\n");
+    
+  }
+  tcp_qprintf(tq, "<hr>\r\n");
+
+  tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
+
+  tcp_qprintf(tq, "<div class=\"infoprefix\">Select:</div><div>");
+
+  ajax_a_jsfunc(tq, "All",                       "select_all();",      " / ");
+  ajax_a_jsfunc(tq, "None",                      "select_none();",     " / ");
+  ajax_a_jsfunc(tq, "Invert",                    "select_invert();",   " / ");
+  ajax_a_jsfunc(tq, "All TV-services",           "select_tv();",       " / ");
+  ajax_a_jsfunc(tq, "All uncrypted TV-services", "select_tv_nocrypt();", "");
+
+  tcp_qprintf(tq, "</div></div>\r\n");
+  
+  tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
+  tcp_qprintf(tq, "<div class=\"infoprefix\">&nbsp;</div><div>");
+
+  ajax_a_jsfunc(tq, "Map selected",    "selected_do('map');",   " / ");
+  ajax_a_jsfunc(tq, "Unmap selected",  "selected_do('unmap');", "");
+
+  tcp_qprintf(tq, "</div></div>");
+
+  tcp_qprintf(tq, "</form>");
+  return 0;
+}
+
+/**
+ *  Rename of unmapped channel
+ */
+static int
+ajax_transport_rename_channel(http_connection_t *hc, 
+                             const char *remain, void *opaque)
+{
+  th_transport_t *t;
+  const char *newname, *v;
+  tcp_queue_t tq;
+  char buf[1000];
+
+  if(remain == NULL || (t = transport_find_by_identifier(remain)) == NULL)
+    return HTTP_STATUS_NOT_FOUND;
+
+  if((newname = http_arg_get(&hc->hc_req_args, "newname")) == NULL)
+    return HTTP_STATUS_BAD_REQUEST;
+
+  free((void *)t->tht_channelname);
+  t->tht_channelname = strdup(newname);
+
+  tcp_init_queue(&tq, -1);
+
+  v = newname;
+
+  snprintf(buf, sizeof(buf), 
+          "tentative_chname('chname%s', "
+          "'/ajax/transport_rename_channel/%s', '%s')",
+          t->tht_identifier, t->tht_identifier, v);
+  
+  ajax_a_jsfunc(&tq, v, buf, "");
+      
+  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  t->tht_config_change(t);
+  return 0;
+}
+
+/**
+ *
+ */
+static void
+dvb_map_channel(th_transport_t *t, tcp_queue_t *tq)
+{
+  transport_set_channel(t, t->tht_channelname);
+
+  printf("Mapped transport %s to channel %s\n",
+        t->tht_servicename, t->tht_channel->ch_name);
+
+  tcp_qprintf(tq, 
+             "document.getElementById('chname%s').innerHTML='%s';\n\r"
+             "document.getElementById('map%s').src='/gfx/mapped.png';\n\r",
+             t->tht_identifier, t->tht_channel->ch_name,
+             t->tht_identifier);
+}
+
+
+/**
+ *
+ */
+static void
+dvb_unmap_channel(th_transport_t *t, tcp_queue_t *tq)
+{
+  transport_unset_channel(t);
+
+  printf("Unmapped transport %s\n", t->tht_servicename);
+
+  tcp_qprintf(tq, 
+             "document.getElementById('chname%s').innerHTML='"
+             "<a href=\"javascript:void(0)\" "
+             "onClick=\"javascript:tentative_chname(\\'chname%s\\', "
+             "\\'/ajax/transport_rename_channel/%s\\', \\'%s\\')\">%s</a>"
+             "';\n\r"
+             "document.getElementById('map%s').src='/gfx/unmapped.png';\n\r",
+             t->tht_identifier, t->tht_identifier, t->tht_identifier,
+             t->tht_channelname, t->tht_channelname, t->tht_identifier);
+}
+
+
+
+/**
+ *
+ */
+int
+ajax_transport_op(http_connection_t *hc, const char *remain, void *opaque)
+{
+  th_transport_t *t;
+  tcp_queue_t tq;
+  const char *op;
+
+  if(remain == NULL || (t = transport_find_by_identifier(remain)) == NULL)
+    return HTTP_STATUS_NOT_FOUND;
+
+  if((op = http_arg_get(&hc->hc_req_args, "action")) == NULL)
+    return HTTP_STATUS_BAD_REQUEST;
+
+  tcp_init_queue(&tq, -1);
+
+  if(!strcmp(op, "toggle")) {
+    if(t->tht_channel)
+      dvb_unmap_channel(t, &tq);
+    else
+      dvb_map_channel(t, &tq);
+  } else if(!strcmp(op, "map") && t->tht_channel == NULL) {
+    dvb_map_channel(t, &tq);
+  } else if(!strcmp(op, "unmap") && t->tht_channel != NULL) {
+    dvb_unmap_channel(t, &tq);
+  }
+
+  http_output_queue(hc, &tq, "text/javascript; charset=UTF-8", 0);
+
+  t->tht_config_change(t);
+  return 0;
+}
+
+
+/**
+ *
+ */
+void
+ajax_config_transport_init(void)
+{
+  http_path_add("/ajax/transport_rename_channel", NULL,
+               ajax_transport_rename_channel);
+
+  http_path_add("/ajax/transport_op", NULL,
+               ajax_transport_op);
+}
diff --git a/ajaxui/images/mapped.png b/ajaxui/images/mapped.png
new file mode 100644 (file)
index 0000000..a449494
Binary files /dev/null and b/ajaxui/images/mapped.png differ
index 12806b126468cdb0e532e2fc45bb758815ac367c..a7adf2d3577fe5837025be1a9c9cb783e8564a40 100644 (file)
Binary files a/ajaxui/images/sbbody_l.gif and b/ajaxui/images/sbbody_l.gif differ
index 2e6cac7dfdfbcc9f29cfea5744e7956b545a6604..51b09101d91fb8dcfb5ba5c334fb53fd7be00416 100644 (file)
Binary files a/ajaxui/images/sbbody_r.gif and b/ajaxui/images/sbbody_r.gif differ
index 9d7d4a1ec0c1c9331b668ad7ed768b3ffb4160fa..c6eb7fdb74cdf8559baac69895db0c17a2a1c4e7 100644 (file)
Binary files a/ajaxui/images/sbhead_r.gif and b/ajaxui/images/sbhead_r.gif differ
diff --git a/ajaxui/images/unmapped.png b/ajaxui/images/unmapped.png
new file mode 100644 (file)
index 0000000..0f2f738
Binary files /dev/null and b/ajaxui/images/unmapped.png differ
index e02e94da1c5d7b407ee9693f08fc554cb0f0d715..33ec9d9e91c8ffcef2523100d5df78d0aaf40966 100644 (file)
@@ -1442,6 +1442,7 @@ Ajax.Updater = Class.create(Ajax.Request, {
         options = this.options;
 
     if (!options.evalScripts) responseText = responseText.stripScripts();
+    if (!$(receiver)) this.stop();
 
     if (receiver = $(receiver)) {
       if (options.insertion) {
index eeb46862b13762fc76325b32fff7c1ad67322563..306c5f7edfd0bc54a6030f412c5657b72792d93b 100644 (file)
@@ -1,14 +1,10 @@
 function switchtab(name, index)
 {
     a = new Ajax.Updater(name + 'deck', '/ajax/' + name + 'tab/'  + index, 
-                        { asynchronous: true, 
-                          method: 'get', 
-                          evalScripts: true});
+                        { method: 'get', evalScripts: true});
 
     a = new Ajax.Updater(name + 'menu', '/ajax/' + name + 'menu/' + index,
-                        { asynchronous: true, 
-                          method: 'get', 
-                          evalScripts: true});
+                        { method: 'get', evalScripts: true});
 };
 
 function updatelistonserver(listid, url, resultid)
@@ -16,8 +12,7 @@ function updatelistonserver(listid, url, resultid)
     //    document.getElementById(resultid).innerHTML = "Updating...";
 
     a = new Ajax.Updater(resultid, url, 
-                        { asynchronous: true, 
-                          evalScripts: true,
+                        { evalScripts: true,
                           parameters:Sortable.serialize(listid)});
 };
 
@@ -27,8 +22,7 @@ function addlistentry(listid, url, name)
        alert("Emtpy name is not allowed");
     } else {
        a = new Ajax.Updater(listid, url,
-                            { asynchronous: true, 
-                              evalScripts: true,
+                            { evalScripts: true,
                               parameters: { name: name },
                               insertion: Insertion.Bottom
                             });
@@ -48,4 +42,27 @@ function addlistentry_by_widget(listid, url, widget)
     name = $F(widget);
     $(widget).clear();
     addlistentry(listid, url, name);
-}
\ No newline at end of file
+}
+
+
+function showhide(name)
+{
+    ctrlname = 'toggle_' + name;
+    if(document.getElementById(ctrlname).innerHTML == 'More') {
+       document.getElementById(ctrlname).innerHTML = 'Less';
+       new Effect.Appear(name, {duration: 0.5});
+    } else {
+       document.getElementById(ctrlname).innerHTML = 'More';
+       new Effect.Fade(name, {duration: 0.5});
+    }
+}
+
+function tentative_chname(id, url, name)
+{
+       var newname = prompt("Enter name of channel", name);
+       if(newname != null && newname != name) {
+               a = new Ajax.Updater(id, url,
+                                { evalScripts: true,
+                                 parameters: { newname: newname }});
+       }
+}
diff --git a/avgen.c b/avgen.c
index 98005d04e21fc048bd325d573116d304c7888251..e9879e9c840a204c0dd35c9b404043d6db7ac26a 100644 (file)
--- a/avgen.c
+++ b/avgen.c
@@ -101,7 +101,6 @@ avgen_init(void)
   ch = channel_find("Test PAL", 1, channel_group_find("Test channels", 1));
 
   t = calloc(1, sizeof(th_transport_t));
-  t->tht_prio = 100;
 
   t->tht_type = TRANSPORT_AVGEN;
   t->tht_start_feed = avgen_start_feed;
@@ -117,9 +116,9 @@ avgen_init(void)
   t->tht_provider = strdup("HTS Tvheadend");
   t->tht_network = strdup("Internal");
 
-  t->tht_uniquename = strdup("TEST1");
+  t->tht_identifier = strdup("test1");
 
-  transport_link(t, ch, THT_OTHER);
+  transport_set_channel(t, ch->ch_name);
 }
 
 
index db6bb5205efb68669c5bf25909a4d7602f04d05e..b2b56f2bf2542565710ad11bf348bd8cc3297738 100644 (file)
@@ -43,7 +43,6 @@ static void channel_group_settings_write(void);
 static void channel_settings_write(th_channel_t *ch);
 
 struct th_channel_list channels;
-struct th_transport_list all_transports;
 int nchannels;
 
 struct th_channel_group_queue all_channel_groups;
@@ -234,8 +233,6 @@ service_load(struct config_head *head)
 
   t = calloc(1, sizeof(th_transport_t));
 
-  t->tht_prio = atoi(config_get_str_sub(head, "prio", ""));
-
   if(0) {
 #ifdef ENABLE_INPUT_IPTV
   } else if((v = config_get_str_sub(head, "iptv", NULL)) != NULL) {
@@ -382,32 +379,6 @@ channel_group_by_tag(uint32_t tag)
   return NULL;
 }
 
-void
-channel_group_move_next(th_channel_group_t *tcg)
-{
-  th_channel_group_t *n = TAILQ_NEXT(tcg, tcg_global_link);
-  if(n == NULL)
-    return;
-
-  TAILQ_REMOVE(&all_channel_groups, tcg, tcg_global_link);
-  TAILQ_INSERT_AFTER(&all_channel_groups, n, tcg, tcg_global_link);
-
-  channel_group_settings_write();
-}
-
-void
-channel_group_move_prev(th_channel_group_t *tcg)
-{
-  th_channel_group_t *p = TAILQ_PREV(tcg, th_channel_group_queue,
-                                    tcg_global_link);
-  if(p == NULL)
-    return;
-
-  TAILQ_REMOVE(&all_channel_groups, tcg, tcg_global_link);
-  TAILQ_INSERT_BEFORE(p, tcg, tcg_global_link);
-  channel_group_settings_write();
-}
-
 
 /**
  * Write out a config file with all channel groups
diff --git a/cwc.c b/cwc.c
index 8a30ace2602148496a2a0ce442c915f2049c84e8..8655a0120280670e583ee9a62b47082578930ebf 100644 (file)
--- a/cwc.c
+++ b/cwc.c
@@ -500,7 +500,7 @@ cwc_dispatch_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
       if(ct->ct_keystate != CT_FORBIDDEN) 
        syslog(LOG_ERR, 
               "Cannot descramble \"%s\" for channel \"%s\", access denied",
-              t->tht_uniquename, t->tht_channel->ch_name);
+              t->tht_identifier, t->tht_channel->ch_name);
 
       ct->ct_keystate = CT_FORBIDDEN;
       return 0;
@@ -509,7 +509,7 @@ cwc_dispatch_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
     if(ct->ct_keystate != CT_RESOLVED)
       syslog(LOG_INFO,
             "Obtained key for \"%s\" for channel \"%s\"",
-            t->tht_uniquename, t->tht_channel->ch_name);
+            t->tht_identifier, t->tht_channel->ch_name);
 
     ct->ct_keystate = CT_RESOLVED;
     set_control_words(ct->ct_keys, msg + 3, msg + 3 + 8);
diff --git a/dvb.c b/dvb.c
index 3c47985c335a9416d9de528a458eea6ab102d2ee..1b7c724e489aeae6f6a063b83aaf9111fa852415 100644 (file)
Binary files a/dvb.c and b/dvb.c differ
diff --git a/dvb.h b/dvb.h
index 7294649971f7b1ff3d6b7fd25578d580e419b5ad..4e01653d6946f45559f5b506076e69963638958f 100644 (file)
--- a/dvb.h
+++ b/dvb.h
@@ -28,9 +28,8 @@ enum polarisation {
 
 #define DVB_FEC_ERROR_LIMIT 20
 
-extern struct th_dvb_adapter_list dvb_adapters_probing;
-extern struct th_dvb_adapter_list dvb_adapters_running;
-extern struct th_dvb_mux_list dvb_muxes;
+extern struct th_dvb_adapter_list dvb_adapters;
+extern struct th_dvb_mux_instance_list dvb_muxes;
 
 void dvb_init(void);
 
@@ -48,7 +47,14 @@ void dvb_fe_start(th_dvb_adapter_t *tda);
 
 void tdmi_check_scan_status(th_dvb_mux_instance_t *tdmi);
 
-th_transport_t *dvb_find_transport(th_dvb_mux_instance_t *tdmi, uint16_t tid,
+th_transport_t *dvb_find_transport(th_dvb_mux_instance_t *tdmi,
                                   uint16_t sid, int pmt_pid);
 
+th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda,
+                                     struct dvb_frontend_parameters *fe_param,
+                                     int polarisation, int switchport,
+                                     int save);
+
+void dvb_tdmi_save(th_dvb_mux_instance_t *tdmi);
+
 #endif /* DVB_H_ */
index c07e15bb22b3894a1e73c3d9274d931643e31467..dd5baff98dfd65bcc2d8acdd9c1287d496452211 100644 (file)
--- a/dvb_dvr.c
+++ b/dvb_dvr.c
@@ -71,7 +71,7 @@ dvb_dvr_process_packets(th_dvb_adapter_t *tda, uint8_t *tsb, int r)
   th_transport_t *t;
 
   while(r >= 188) {
-    LIST_FOREACH(t, &tda->tda_transports, tht_adapter_link)
+    LIST_FOREACH(t, &tda->tda_transports, tht_active_link)
       ts_recv_packet1(t, tsb);
     r -= 188;
     tsb += 188;
@@ -120,7 +120,7 @@ dvb_stop_feed(th_transport_t *t)
 {
   th_stream_t *st;
 
-  LIST_REMOVE(t, tht_adapter_link);
+  LIST_REMOVE(t, tht_active_link);
   LIST_FOREACH(st, &t->tht_streams, st_link) {
     close(st->st_demuxer_fd);
     st->st_demuxer_fd = -1;
@@ -199,7 +199,7 @@ dvb_start_feed(th_transport_t *t, unsigned int weight, int status)
     st->st_demuxer_fd = fd;
   }
 
-  LIST_INSERT_HEAD(&tda->tda_transports, t, tht_adapter_link);
+  LIST_INSERT_HEAD(&tda->tda_transports, t, tht_active_link);
   t->tht_status = status;
   
   dvb_tune_tdmi(tdmi, 1, TDMI_RUNNING);
index 1d11f9f6629a69344e8980cb89620231baca1851..639e8eee9730cf2bb47f107f739490290a1c82a7 100644 (file)
--- a/dvb_fe.c
+++ b/dvb_fe.c
@@ -83,7 +83,7 @@ dvb_fe_manager(void *aux)
 
       p = *tdmi->tdmi_fe_params;
 
-      if(tdmi->tdmi_type == FE_QPSK) {
+      if(tda->tda_fe_info->type == FE_QPSK) {
        /* DVB-S */
        int lowfreq, hifreq, switchfreq, hiband;
 
@@ -105,9 +105,8 @@ dvb_fe_manager(void *aux)
        else
          p.frequency = abs(p.frequency - lowfreq);
       }
-
       i = ioctl(tda->tda_fe_fd, FE_SET_FRONTEND, &p);
-
       if(i != 0) {
        syslog(LOG_ERR, "\"%s\" tuning to %dHz"
               " -- Front configuration failed -- %s",
@@ -160,6 +159,8 @@ dvb_fe_manager(void *aux)
       tdmi->tdmi_status = "No signal";
 
     ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &v);
+    if(v < 0)
+      v = 0;
 
     if(fe_status & FE_HAS_LOCK) {
       tdmi->tdmi_fec_err_histogram[tdmi->tdmi_fec_err_ptr] = v;
@@ -223,7 +224,7 @@ dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, tdmi_state_t state)
 
   if(maylog)
     syslog(LOG_DEBUG, "\"%s\" tuning to mux \"%s\"", 
-          tda->tda_rootpath, tdmi->tdmi_shortname);
+          tda->tda_rootpath, "FIXME");
 
   /* Add tables which will be activated once the tuning is completed */
 
index 28451878e7b618ae7177b09c7d9b6c104701733f..4cf7f9f325b101c096fbfcd609512ebe78d66813 100644 (file)
 #include "dvb.h"
 #include "dvb_dvr.h"
 #include "dvb_muxconfig.h"
+#include "dvb_support.h"
 #include "strtab.h"
 #include "transports.h"
 
-
-
-static void
-dvb_add_mux(struct dvb_frontend_parameters *fe_param, fe_type_t type,
-           int polarisation)
-{
-  th_dvb_adapter_t *tda;
-  char buf[100];
-  char *typetxt;
-
-  th_dvb_mux_instance_t *tdmi;
-
-  switch(type) {
-  case FE_QPSK:
-    typetxt = "DVB-S";
-    break;
-  case FE_QAM:
-    typetxt = "DVB-C";
-    break;
-  case FE_OFDM:
-    typetxt = "DVB-T";
-    break;
-  case FE_ATSC:
-    typetxt = "ATSC";
-    break;
-  default:
-    return;
-  }
-
-  LIST_FOREACH(tda, &dvb_adapters_probing, tda_link) {
-    if(tda->tda_fe_info->type != type)
-      continue; /* Does not match frontend */
-
-    tdmi = calloc(1, sizeof(th_dvb_mux_instance_t));
-    pthread_mutex_init(&tdmi->tdmi_table_lock, NULL);
-
-    tdmi->tdmi_status = TDMI_CONFIGURED;
-    tdmi->tdmi_adapter = tda;
-    LIST_INSERT_HEAD(&tda->tda_muxes_configured, tdmi, tdmi_adapter_link);
-
-    tdmi->tdmi_type = type;
-
-    tdmi->tdmi_fe_params = malloc(sizeof(struct dvb_frontend_parameters));
-    tdmi->tdmi_polarisation = polarisation;
-
-    memcpy(tdmi->tdmi_fe_params, fe_param,
-          sizeof(struct dvb_frontend_parameters));
-    
-    /* Generate names */
-
-    if(tdmi->tdmi_type == FE_QPSK) {
-      const char *pol;
-
-      switch(polarisation) {
-      case POLARISATION_VERTICAL:   pol = "V"; break;
-      case POLARISATION_HORIZONTAL: pol = "H"; break;
-      default:                      pol = "X"; break;
-      }
-
-
-      /* Frequency is in kHz */
-      snprintf(buf, sizeof(buf), "%s/%.1fMHz/%s",
-              typetxt, (float)fe_param->frequency / 1000.0f, pol);
-    } else {
-      snprintf(buf, sizeof(buf), "%s/%.1fMHz",
-              typetxt, (float)fe_param->frequency / 1000000.0f);
-    }
-      tdmi->tdmi_shortname = strdup(buf);
-    
-    snprintf(buf, sizeof(buf), "%s/%s", tda->tda_sname, tdmi->tdmi_shortname),
-    tdmi->tdmi_uniquename = strdup(buf);
-
-  }
-}
-
-
-
 static struct strtab fectab[] = {
   { "NONE", FEC_NONE },
   { "1/2",  FEC_1_2 },
@@ -148,7 +72,6 @@ static struct strtab modetab[] = {
   { "AUTO", TRANSMISSION_MODE_AUTO }
 };
 
-
 static struct strtab guardtab[] = {
   { "1/32", GUARD_INTERVAL_1_32 },
   { "1/16", GUARD_INTERVAL_1_16 },
@@ -165,6 +88,174 @@ static struct strtab hiertab[] = {
   { "AUTO", HIERARCHY_AUTO }
 };
 
+static struct strtab poltab[] = {
+  { "V",    POLARISATION_VERTICAL },
+  { "H",    POLARISATION_HORIZONTAL },
+};
+
+
+void
+dvb_mux_store(FILE *fp, th_dvb_mux_instance_t *tdmi)
+{
+  struct dvb_frontend_parameters *f = tdmi->tdmi_fe_params;
+
+  fprintf(fp, "\tfrequency = %d\n", f->frequency);
+  
+  switch(tdmi->tdmi_adapter->tda_fe_info->type) {
+  case FE_OFDM:
+    fprintf(fp, "\tbandwidth = %s\n",     
+           val2str(f->u.ofdm.bandwidth, bwtab));
+
+    fprintf(fp, "\tconstellation = %s\n", 
+           val2str(f->u.ofdm.constellation, qamtab));
+
+    fprintf(fp, "\ttransmission_mode = %s\n", 
+           val2str(f->u.ofdm.transmission_mode, modetab));
+
+    fprintf(fp, "\tguard_interval = %s\n", 
+           val2str(f->u.ofdm.guard_interval, guardtab));
+
+    fprintf(fp, "\thierarchy = %s\n", 
+           val2str(f->u.ofdm.hierarchy_information, hiertab));
+
+    fprintf(fp, "\tfec_hi = %s\n", 
+           val2str(f->u.ofdm.code_rate_HP, fectab));
+
+    fprintf(fp, "\tfec_lo = %s\n", 
+           val2str(f->u.ofdm.code_rate_LP, fectab));
+    break;
+
+  case FE_QPSK:
+    fprintf(fp, "\tsymbol_rate = %d\n", f->u.qpsk.symbol_rate);
+
+    fprintf(fp, "\tfec = %s\n", 
+           val2str(f->u.qpsk.fec_inner, fectab));
+
+    fprintf(fp, "\tpolarisation = %s\n", 
+           val2str(tdmi->tdmi_polarisation, poltab));
+    fprintf(fp, "\tswitchport = %d\n", tdmi->tdmi_switchport);
+    break;
+
+  case FE_QAM:
+    fprintf(fp, "\tsymbol_rate = %d\n", f->u.qam.symbol_rate);
+
+    fprintf(fp, "\tfec = %s\n", 
+           val2str(f->u.qam.fec_inner, fectab));
+
+    fprintf(fp, "\tconstellation = %s\n", 
+           val2str(f->u.qam.modulation, qamtab));
+    break;
+
+  case FE_ATSC:
+    break;
+  }
+}
+
+/**
+ *
+ */
+const char *
+dvb_mux_create_str(th_dvb_adapter_t *tda,
+                  const char *freqstr,
+                  const char *symratestr,
+                  const char *qamstr,
+                  const char *fecstr,
+                  const char *fechistr,
+                  const char *feclostr,
+                  const char *bwstr,
+                  const char *tmodestr,
+                  const char *guardstr,
+                  const char *hierstr,
+                  const char *polstr,
+                  const char *switchportstr,
+                  int save)
+{
+  struct dvb_frontend_parameters f;
+  int r;
+  int polarisation = 0, switchport = 0;
+
+  memset(&f, 0, sizeof(f));
+  
+  f.inversion = INVERSION_AUTO;
+
+  f.frequency = freqstr ? atoi(freqstr) : 0;
+  if(f.frequency == 0)
+    return "Invalid frequency";
+
+  switch(tda->tda_fe_info->type) {
+  case FE_OFDM:
+    if(bwstr == NULL || (r = str2val(bwstr, bwtab)) < 0)
+      return "Invalid bandwidth";
+    f.u.ofdm.bandwidth = r;
+
+    if(qamstr == NULL || (r = str2val(qamstr, qamtab)) < 0)
+      return "Invalid QAM constellation";
+    f.u.ofdm.constellation = r;
+
+    if(tmodestr == NULL || (r = str2val(tmodestr, modetab)) < 0)
+      return "Invalid transmission mode";
+    f.u.ofdm.transmission_mode = r;
+
+    if(guardstr == NULL || (r = str2val(guardstr, guardtab)) < 0)
+      return "Invalid guard interval";
+    f.u.ofdm.guard_interval = r;
+
+    if(hierstr == NULL || (r = str2val(hierstr, hiertab)) < 0)
+      return "Invalid heirarchy information";
+    f.u.ofdm.hierarchy_information = r;
+
+    if(fechistr == NULL || (r = str2val(fechistr, fectab)) < 0)
+      printf("hifec = %s\n", fechistr);
+    f.u.ofdm.code_rate_HP = r;
+
+    if(feclostr == NULL || (r = str2val(feclostr, fectab)) < 0)
+      return "Invalid lo-FEC";
+    f.u.ofdm.code_rate_LP = r;
+    break;
+
+  case FE_QPSK:
+    f.u.qpsk.symbol_rate = symratestr ? atoi(symratestr) : 0;
+    if(f.u.qpsk.symbol_rate == 0)
+      return "Invalid symbol rate";
+    
+    if(fecstr == NULL || (r = str2val(fecstr, fectab)) < 0)
+      return "Invalid FEC";
+    f.u.qpsk.fec_inner = r;
+
+    if(polstr == NULL || (r = str2val(polstr, poltab)) < 0)
+      return "Invalid polarisation";
+    polarisation = r;
+    
+    switchport = atoi(switchportstr);
+    break;
+
+  case FE_QAM:
+    f.u.qam.symbol_rate = symratestr ? atoi(symratestr) : 0;
+    if(f.u.qam.symbol_rate == 0)
+      return "Invalid symbol rate";
+    
+    if(qamstr == NULL || (r = str2val(qamstr, qamtab)) < 0)
+      return "Invalid QAM constellation";
+    f.u.qam.modulation = r;
+
+    if(fecstr == NULL || (r = str2val(fecstr, fectab)) < 0)
+      return "Invalid FEC";
+    f.u.qam.fec_inner = r;
+    break;
+
+  case FE_ATSC:
+    break;
+  }
+
+  dvb_mux_create(tda, &f, polarisation, switchport, save);
+
+  return NULL;
+}
+
+
+#if 0
+
 
 
 static void
@@ -323,3 +414,4 @@ dvb_mux_setup(void)
       dvb_muxfile_add(ce->ce_value);
 }
 
+#endif
index 64684dc719360fc8c31fc498683097e73ca4b2dd..dcdd20fc2691da3fa5cf6e868c5ad3073dd9fa9e 100644 (file)
 #ifndef DVB_MUXCONFIG_H_
 #define DVB_MUXCONFIG_H_
 
-void dvb_mux_setup(void);
+void dvb_mux_store(FILE *fp, th_dvb_mux_instance_t *tdmi);
+
+const char *dvb_mux_create_str(th_dvb_adapter_t *tda,
+                              const char *freqstr,
+                              const char *symratestr,
+                              const char *qamstr,
+                              const char *fecstr,
+                              const char *fechistr,
+                              const char *feclostr,
+                              const char *bwstr,
+                              const char *tmodestr,
+                              const char *guardstr,
+                              const char *hierstr,
+                              const char *polstr,
+                              const char *switchportstr,
+                              int save);
 
 #endif /* DVB_MUXCONFIG_H */
index 80bf523e158972799a0e867a0694bf8237386786..3611e3da8a7ca00b20e274484010d7bd1ce8d90b 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include <linux/dvb/frontend.h>
+
 #include "tvhead.h"
 #include "dvb_support.h"
+#include "strtab.h"
+#include "dvb.h"
 
 /*
  * DVB String conversion according to EN 300 468, Annex A
@@ -205,3 +209,83 @@ dvb_convert_date(uint8_t *dvb_buf)
   dvb_time.tm_yday = 0;
   return (timegm(&dvb_time));
 }
+
+/**
+ *
+ */
+static struct strtab adaptertype[] = {
+  { "DVB-S",  FE_QPSK },
+  { "DVB-C",  FE_QAM },
+  { "DVB-T",  FE_OFDM },
+  { "ATSC",   FE_ATSC },
+};
+
+const char *
+dvb_adaptertype_to_str(int type)
+{
+  return val2str(type, adaptertype) ?: "invalid";
+}
+
+const char *
+dvb_polarisation_to_str(int pol)
+{
+  switch(pol) {
+  case POLARISATION_VERTICAL:   return "V";
+  case POLARISATION_HORIZONTAL: return "H";
+  default:                      return "X";
+  }
+}
+
+
+
+th_dvb_mux_instance_t *
+dvb_mux_find_by_identifier(const char *identifier)
+{
+  th_dvb_mux_instance_t *tdmi;
+
+  LIST_FOREACH(tdmi, &dvb_muxes, tdmi_global_link)
+    if(!strcmp(identifier, tdmi->tdmi_identifier))
+      return tdmi;
+  return NULL;
+}
+
+
+
+th_dvb_adapter_t *
+dvb_adapter_find_by_identifier(const char *identifier)
+{
+  th_dvb_adapter_t *tda;
+
+  LIST_FOREACH(tda, &dvb_adapters, tda_global_link)
+    if(!strcmp(identifier, tda->tda_identifier))
+      return tda;
+  return NULL;
+}
+
+
+
+
+/**
+ * 
+ */
+const char *
+dvb_mux_status(th_dvb_mux_instance_t *tdmi)
+{
+  int i, v, vv;
+  const char *txt = tdmi->tdmi_status ?: "Ok";
+
+  v = vv = 0;
+  for(i = 0; i < TDMI_FEC_ERR_HISTOGRAM_SIZE; i++) {
+    if(tdmi->tdmi_fec_err_histogram[i] > DVB_FEC_ERROR_LIMIT)
+      v++;
+    vv += tdmi->tdmi_fec_err_histogram[i];
+  }
+  vv /= TDMI_FEC_ERR_HISTOGRAM_SIZE;
+  
+  if(v == TDMI_FEC_ERR_HISTOGRAM_SIZE)
+    txt = "Constant high FEC rate";
+  else if(v > 0)
+    txt = "Bursty FEC rate";
+
+  return txt;
+}
index 2cef686ba9f416050782b9dc706cf7689f1e39eb..3d0c3ade871bdb3fb77e00299d577ee3bf7c993f 100644 (file)
@@ -34,6 +34,7 @@
 
 #define DVB_DESC_NETWORK_NAME 0x40
 #define DVB_DESC_SERVICE_LIST 0x41
+#define DVB_DESC_CABLE        0x44
 #define DVB_DESC_SHORT_EVENT  0x4d
 #define DVB_DESC_SERVICE      0x48
 #define DVB_DESC_CONTENT      0x54
 #define DVB_DESC_SUBTITLE     0x59
 #define DVB_DESC_AC3          0x6a
 
-
-/* Service types defined in EN 300 468 */
-
-#define DVB_ST_SDTV           0x1    /* SDTV (MPEG2) */
-#define DVB_ST_RADIO          0x2
-#define DVB_ST_HDTV           0x11   /* HDTV (MPEG2) */
-#define DVB_ST_AC_SDTV        0x16   /* Advanced codec SDTV */
-#define DVB_ST_AC_HDTV        0x19   /* Advanced codec HDTV */
-
 int dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, 
                   const size_t srclen, const char *target_encoding);
 
@@ -61,4 +53,10 @@ int dvb_get_string_with_len(char *dst, size_t dstlen,
 
 time_t dvb_convert_date(uint8_t *dvb_buf);
 
+const char *dvb_adaptertype_to_str(int type);
+const char *dvb_polarisation_to_str(int pol);
+th_dvb_adapter_t *dvb_adapter_find_by_identifier(const char *identifier);
+th_dvb_mux_instance_t *dvb_mux_find_by_identifier(const char *identifier);
+const char *dvb_mux_status(th_dvb_mux_instance_t *tdmi);
+
 #endif /* DVB_SUPPORT_H */
index 383cd62249f0462414075e24074094b9f74b16a4..46a2ab2002e823bb565bc9592de9d9d4dc4b5836 100644 (file)
@@ -92,10 +92,7 @@ dvb_table_recv(int events, void *opaque, int fd)
   ptr = &sec[3];
   len -= 4;   /* Strip trailing CRC */
 
-  if(!tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque))
-    tdt->tdt_count++;
-
-  tdmi_check_scan_status(tdt->tdt_tdmi);
+  tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque);
 }
 
 
@@ -107,9 +104,9 @@ dvb_table_recv(int events, void *opaque, int fd)
  */
 static void
 tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams,
-       int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
-                       uint8_t tableid, void *opaque), void *opaque,
-       int initial_count, char *name, int flags)
+       void (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
+                        uint8_t tableid, void *opaque), void *opaque,
+       char *name, int flags)
 {
   th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
   th_dvb_table_t *tdt;
@@ -125,7 +122,6 @@ tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams,
   tdt->tdt_opaque = opaque;
   tdt->tdt_tdmi = tdmi;
   tdt->tdt_handle = dispatch_addfd(fd, dvb_table_recv, tdt, DISPATCH_READ);
-  tdt->tdt_count = initial_count;
  
   if(flags & TDT_NOW) {
     ioctl(fd, DMX_SET_FILTER, fparams);
@@ -199,7 +195,7 @@ dvb_desc_service(uint8_t *ptr, int len, uint8_t *typep,
 /**
  * DVB EIT (Event Information Table)
  */
-static int
+static void
 dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
                 uint8_t tableid, void *opaque)
 {
@@ -227,11 +223,8 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
   char desc[5000];
   epg_content_type_t *ect;
 
-  if(tableid < 0x4e || tableid > 0x6f)
-    return -1;
-
-  if(len < 11)
-    return -1;
+  if(tableid < 0x4e || tableid > 0x6f || len < 11)
+    return;
 
   serviceid                   = ptr[0] << 8 | ptr[1];
   version                     = ptr[2] >> 1 & 0x1f;
@@ -246,12 +239,13 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
   len -= 11;
   ptr += 11;
 
-  t = dvb_find_transport(tdmi, transport_stream_id, serviceid, 0);
+  t = dvb_find_transport(tdmi, serviceid, 0);
   if(t == NULL)
-    return -1;
+    return;
+
   ch = t->tht_channel;
   if(ch == NULL)
-    return -1;
+    return;
 
   epg_lock();
 
@@ -307,14 +301,13 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
   }
   
   epg_unlock();
-  return 0;
 }
 
 
 /**
  * DVB SDT (Service Description Table)
  */
-static int
+static void
 dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
                 uint8_t tableid, void *opaque)
 {
@@ -335,13 +328,11 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
   char provider[256];
   char chname0[256], *chname;
   uint8_t stype;
-  int ret = 0, l;
-
-  if(tdmi->tdmi_network == NULL)
-    return -1;
+  int l;
+  int change = 0;
 
   if(len < 8)
-    return -1;
+    return;
 
   transport_stream_id         = ptr[0] << 8 | ptr[1];
   version                     = ptr[2] >> 1 & 0x1f;
@@ -385,11 +376,10 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
       case DVB_DESC_SERVICE:
        if(dvb_desc_service(ptr, dlen, &stype,
                            provider, sizeof(provider),
-                           chname0, sizeof(chname0)) < 0) {
-         stype = 0;
-       } else {
+                           chname0, sizeof(chname0)) == 0) {
          chname = chname0;
-         /* Some providers insert spaces */
+         /* Some providers insert spaces.
+            Clean up that (both heading and trailing) */
          while(*chname <= 32 && *chname != 0)
            chname++;
 
@@ -399,70 +389,51 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
            l--;
          }
 
-       }
-       break;
-      }
+         t = dvb_find_transport(tdmi, service_id, 0);
+         if(t == NULL)
+           break;
 
-      len -= dlen; ptr += dlen; dllen -= dlen;
-    }
+         change |=
+           t->tht_servicetype != stype ||
+           t->tht_scrambled != free_ca_mode ||
+           strcmp(t->tht_provider    ?: "", provider) ||
+           strcmp(t->tht_servicename ?: "", chname  );
 
-    
-    if(chname != NULL) switch(stype) {
+         t->tht_servicetype = stype;
+         t->tht_scrambled = free_ca_mode;
 
-    case DVB_ST_SDTV:
-    case DVB_ST_HDTV:
-    case DVB_ST_AC_SDTV:
-    case DVB_ST_AC_HDTV:
-      /* TV service */
+         free((void *)t->tht_provider);
+         t->tht_provider = strdup(provider);
 
-      t = dvb_find_transport(tdmi, transport_stream_id, service_id, 0);
-      
-      if(t == NULL) {
-       ret |= 1;
-      } else {
-       t->tht_scrambled = free_ca_mode;
-
-       free((void *)t->tht_provider);
-       t->tht_provider = strdup(provider);
-
-       free((void *)t->tht_network);
-       t->tht_network = strdup(tdmi->tdmi_network);
-
-       if(t->tht_channel == NULL) {
-         /* Not yet mapped to a channel */
-         if(LIST_FIRST(&t->tht_streams) != NULL) {
-           /* We have streams, map it */
-           if(t->tht_scrambled)
-             t->tht_prio = 75;
-           else
-             t->tht_prio = 25;
-
-           transport_set_channel(t, channel_find(chname, 1, NULL));
-         } else {
-           if(t->tht_pmt_seen == 0)
-             ret |= 1; /* Return error (so scanning wont continue yet) */
-         }
+         free((void *)t->tht_servicename);
+         t->tht_servicename = strdup(chname);
+
+         if(t->tht_channelname == NULL)
+           t->tht_channelname = strdup(chname);
        }
+       break;
       }
-      break;
 
+      len -= dlen; ptr += dlen; dllen -= dlen;
     }
   }
-  return ret;
+  if(change)
+    dvb_tdmi_save(tdmi);
 }
 
 
 /**
  * PAT - Program Allocation table
  */
-static int
+static void
 dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
                 uint8_t tableid, void *opaque)
 {
   uint16_t service, pmt, tid;
+  th_transport_t *t;
 
   if(len < 5)
-    return -1;
+    return;
 
   tid = (ptr[0] << 8) | ptr[1];
 
@@ -472,21 +443,21 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
   while(len >= 4) {
     service =  ptr[0]         << 8 | ptr[1];
     pmt     = (ptr[2] & 0x1f) << 8 | ptr[3];
-    
-    if(service != 0)
-      dvb_find_transport(tdmi, tid, service, pmt);
 
+    if(service != 0) {
+      t = dvb_find_transport(tdmi, service, pmt);
+      dvb_table_add_transport(tdmi, t, pmt); /* Add PMT to our table scanner */
+    }
     ptr += 4;
     len -= 4;
   }
-  return 0;
 }
 
 
 /**
  * CAT - Condition Access Table
  */
-static int
+static void
 dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
                 uint8_t tableid, void *opaque)
 {
@@ -514,32 +485,86 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
     ptr += tlen;
     len -= tlen;
   }
-  return 0;
 }
 
 
+/**
+ * Tables for delivery descriptor parsing
+ */
+static const fe_code_rate_t fec_tab [8] = {
+  FEC_AUTO, FEC_1_2, FEC_2_3, FEC_3_4,
+  FEC_5_6, FEC_7_8, FEC_NONE, FEC_NONE
+};
+
+
+static const fe_modulation_t qam_tab [6] = {
+        QAM_AUTO, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256
+};
+
+/**
+ * Cable delivery descriptor
+ */
+static void
+dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len)
+{
+  int freq, symrate;
+  struct dvb_frontend_parameters fe_param;
+
+  if(len < 11) {
+    printf("Invalid CABLE DESCRIPTOR\n");
+    return;
+  }
+  memset(&fe_param, 0, sizeof(fe_param));
+  fe_param.inversion = INVERSION_AUTO;
+
+  freq =
+    bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 + 
+    bcdtoint(ptr[2]) * 100     + bcdtoint(ptr[3]);
+
+  fe_param.frequency = freq * 100;
+
+  symrate =
+    bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 + 
+    bcdtoint(ptr[9]) * 10     + (ptr[10] >> 4);
+
+  fe_param.u.qam.symbol_rate = symrate * 100;
+
+
+  if((ptr[6] & 0x0f) > 5)
+    fe_param.u.qam.modulation = QAM_AUTO;
+  else
+    fe_param.u.qam.modulation = qam_tab[ptr[6] & 0x0f];
+
+  fe_param.u.qam.fec_inner = fec_tab[ptr[10] & 0x07];
+
+  dvb_mux_create(tdmi->tdmi_adapter, &fe_param, 0, 0, 1);
+}
+
+
+
 /**
  * NIT - Network Information Table
  */
-static int
+static void
 dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
                 uint8_t tableid, void *opaque)
 {
   uint8_t tag, tlen;
   int ntl;
   char networkname[256];
+  uint16_t tsid;
 
   ptr += 5;
   len -= 5;
 
   if(tableid != 0x40)
-    return -1;
+    return;
 
   ntl = ((ptr[0] & 0xf) << 8) | ptr[1];
   ptr += 2;
   len -= 2;
   if(ntl > len)
-    return 0;
+    return;
 
   while(ntl > 2) {
     tag = *ptr++;
@@ -550,7 +575,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
     switch(tag) {
     case DVB_DESC_NETWORK_NAME:
       if(dvb_get_string(networkname, sizeof(networkname), ptr, tlen, "UTF8"))
-       return 0;
+       return;
 
       free((void *)tdmi->tdmi_network);
       tdmi->tdmi_network = strdup(networkname);
@@ -562,7 +587,42 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
     ntl -= tlen;
   }
 
-  return 0;
+  if(len < 2)
+    return;
+
+  ntl =  ((ptr[0] & 0xf) << 8) | ptr[1];
+  ptr += 2;
+  len -= 2;
+
+  if(len < ntl)
+    return;
+
+  while(len >= 6) {
+    tsid = ( ptr[0]        << 8) | ptr[1];
+    ntl =  ((ptr[4] & 0xf) << 8) | ptr[5];
+
+    ptr += 6;
+    len -= 6;
+    if(ntl > len)
+      break;
+
+    while(ntl > 2) {
+      tag = *ptr++;
+      tlen = *ptr++;
+      len -= 2;
+      ntl -= 2;
+
+      switch(tag) {
+      case DVB_DESC_CABLE:
+       dvb_table_cable_delivery(tdmi, ptr, tlen);
+       break;
+      }
+
+      ptr += tlen;
+      len -= tlen;
+      ntl -= tlen;
+    }
+  }
 }
 
 
@@ -570,31 +630,35 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
 /**
  * PMT - Program Mapping Table
  */
-static int
+static void
 dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
                 uint8_t tableid, void *opaque)
 {
   th_transport_t *t = opaque;
+  int v = t->tht_pmt_seen;
 
-  return psi_parse_pmt(t, ptr, len, 1);
+  psi_parse_pmt(t, ptr, len, 1);
+  v ^= t->tht_pmt_seen;
+  if(v)
+    dvb_tdmi_save(tdmi);
+  return;
 }
 
 
 /**
  * RST - Running Status Table
  */
-static int
+static void
 dvb_rst_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
                 uint8_t tableid, void *opaque)
 {
   int i;
 
-  printf("Got RST on %s\t", tdmi->tdmi_uniquename);
+  //  printf("Got RST on %s\t", tdmi->tdmi_uniquename);
 
   for(i = 0; i < len; i++)
     printf("%02x.", ptr[i]);
   printf("\n");
-  return 0;
 }
 
 
@@ -628,38 +692,38 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi)
   fp = dvb_fparams_alloc(0x0, DMX_IMMEDIATE_START | DMX_CHECK_CRC);
   fp->filter.filter[0] = 0x00;
   fp->filter.mask[0] = 0xff;
-  tdt_add(tdmi, fp, dvb_pat_callback, NULL, 0, "pat", 0);
+  tdt_add(tdmi, fp, dvb_pat_callback, NULL, "pat", 0);
 
   /* Conditional Access Table */
 
   fp = dvb_fparams_alloc(0x1, DMX_IMMEDIATE_START | DMX_CHECK_CRC);
   fp->filter.filter[0] = 0x1;
   fp->filter.mask[0] = 0xff;
-  tdt_add(tdmi, fp, dvb_cat_callback, NULL, 1, "cat", 0);
+  tdt_add(tdmi, fp, dvb_cat_callback, NULL, "cat", 0);
 
   /* Network Information Table */
 
   fp = dvb_fparams_alloc(0x10, DMX_IMMEDIATE_START | DMX_CHECK_CRC);
-  tdt_add(tdmi, fp, dvb_nit_callback, NULL, 0, "nit", 0);
+  tdt_add(tdmi, fp, dvb_nit_callback, NULL, "nit", 0);
 
   /* Service Descriptor Table */
 
   fp = dvb_fparams_alloc(0x11, DMX_IMMEDIATE_START | DMX_CHECK_CRC);
   fp->filter.filter[0] = 0x42;
   fp->filter.mask[0] = 0xff;
-  tdt_add(tdmi, fp, dvb_sdt_callback, NULL, 0, "sdt", 0);
+  tdt_add(tdmi, fp, dvb_sdt_callback, NULL, "sdt", 0);
 
   /* Event Information table */
 
   fp = dvb_fparams_alloc(0x12, DMX_IMMEDIATE_START | DMX_CHECK_CRC);
-  tdt_add(tdmi, fp, dvb_eit_callback, NULL, 1, "eit", 0);
+  tdt_add(tdmi, fp, dvb_eit_callback, NULL, "eit", 0);
 
   /* Running Status Table */
 
   fp = dvb_fparams_alloc(0x13, DMX_IMMEDIATE_START | DMX_CHECK_CRC);
   fp->filter.filter[0] = 0x71;
   fp->filter.mask[0] = 0xff;
-  tdt_add(tdmi, fp, dvb_rst_callback, NULL, 1, "rst", 0);
+  tdt_add(tdmi, fp, dvb_rst_callback, NULL, "rst", 0);
 
 }
 
@@ -680,5 +744,5 @@ dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t,
   fp = dvb_fparams_alloc(pmt_pid, DMX_IMMEDIATE_START | DMX_CHECK_CRC);
   fp->filter.filter[0] = 0x02;
   fp->filter.mask[0] = 0xff;
-  tdt_add(tdmi, fp, dvb_pmt_callback, t, 0, pmtname, TDT_NOW);
+  tdt_add(tdmi, fp, dvb_pmt_callback, t, pmtname, TDT_NOW);
 }
index e3f231d11ea297f0274fd34a2ed30f9a622c5948..30e4d3159b49ef42dd1bc7c40721c9df4b486e27 100644 (file)
@@ -112,7 +112,6 @@ file_input_init(void)
     fi->fi_name = strdup(s);
     
     t = calloc(1, sizeof(th_transport_t));
-    t->tht_prio = 100;
 
     t->tht_type = TRANSPORT_STREAMEDFILE;
     t->tht_start_feed = file_input_start_feed;
@@ -156,9 +155,9 @@ file_input_init(void)
     t->tht_name = strdup(ch->ch_name);
     t->tht_provider = strdup("HTS Tvheadend");
     t->tht_network = strdup("Internal");
-    t->tht_uniquename = strdup(ch->ch_name);
+    t->tht_identifier = strdup(ch->ch_name);
     t->tht_file_input = fi;
-    transport_link(t, ch, THT_OTHER);
+    transport_set_channel(t, ch->ch_name);
   }
 }
 
index 95a070eca01e5efa604ec0a4377fb352d06fd591..f240c6581b42aad77aced8c021ff1430e4286fc8 100644 (file)
@@ -144,203 +144,6 @@ clients_send_ref(int ref)
   }
 }
 
-/*
- *
- */
-static void
-print_tdmi(client_t *c, th_dvb_mux_instance_t *tdmi)
-{
-  switch(tdmi->tdmi_state) {
-  case TDMI_CONFIGURED:
-    cprintf(c, "Configured, awaiting scan slot\n");
-    return;
-
-  case TDMI_INITIAL_SCAN:
-    cprintf(c, "Initial scan\n");
-    return;
-
-  case TDMI_IDLE:
-    cprintf(c, "   Idle since %s", ctime(&tdmi->tdmi_lost_adapter));
-    cprintf(c, "\tLast known status: ");
-    break;
-
-  case TDMI_RUNNING:
-    cprintf(c, "Running since %s", ctime(&tdmi->tdmi_got_adapter));
-    cprintf(c, "\t   Current status: ");
-    break;
-
-  case TDMI_IDLESCAN:
-    cprintf(c, "Idle but scanning\n");
-    cprintf(c, "\t   Current status: ");
-    break;
-  }
-
-  if(tdmi->tdmi_status != NULL) {
-    cprintf(c, "%s\n", tdmi->tdmi_status);
-    return;
-  }
-
-  cprintf(c, "locked\n");
-}
-
-/*
- *
- */
-static int
-cr_show(client_t *c, char **argv, int argc)
-{
-  const char *subcmd;
-  th_subscription_t *s;
-  th_transport_t *t;
-  th_channel_t *ch;
-  th_dvb_adapter_t *tda;
-  th_dvb_mux_instance_t *tdmi;
-  event_t *e;
-  char *tmp;
-  char *txt;
-  int v, remain;
-
-  if(argc != 1)
-    return 1;
-  
-  subcmd = argv[0];
-
-  if(!strcasecmp(subcmd, "subscriptions")) {
-    cprintf(c, "prio %-18s %-20s %s\n", "output", "channel", "source");
-    cprintf(c, "-----------------------------------------------------------------------------\n");
-
-    LIST_FOREACH(s, &subscriptions, ths_global_link) {
-
-      cprintf(c, "%4d %-18s %-20s %s\n", 
-             s->ths_weight,
-             s->ths_title, s->ths_channel->ch_name,
-             s->ths_transport ? s->ths_transport->tht_name : "no transport");
-    }
-    return 0;
-  }
-
-  if(!strcasecmp(subcmd, "channel")) {
-    
-    LIST_FOREACH(ch, &channels, ch_global_link) {
-
-      tmp = utf8toprintable(ch->ch_name);
-      cprintf(c, "%3d: \"%s\"\n", ch->ch_index, tmp);
-      free(tmp);
-
-      epg_lock();
-
-      e = epg_event_get_current(ch);
-
-      if(e != NULL) {
-
-       tmp = utf8toprintable(e->e_title ?: "<no current event>");
-
-       remain = e->e_start + e->e_duration - time(NULL);
-       remain /= 60;
-
-       switch(e->e_source) {
-       case EVENT_SRC_XMLTV:
-         txt = "xmltv";
-         break;
-       case EVENT_SRC_DVB:
-         txt = "dvb";
-         break;
-       default:
-         txt = "???";
-         break;
-       }
-
-       cprintf(c, "\tNow: %-40s %2d:%02d [%s] tag: %d\n", 
-               tmp, remain / 60, remain % 60, txt, e->e_tag);
-       free(tmp);
-      }
-
-      epg_unlock();
-
-      LIST_FOREACH(t, &ch->ch_transports, tht_channel_link) {
-
-       cprintf(c, "\t%-47s", t->tht_name);
-
-       switch(t->tht_status) {
-       case TRANSPORT_IDLE:
-         cprintf(c, "idle\n");
-         break;
-
-       case TRANSPORT_RUNNING:
-         v = avgstat_read_and_expire(&t->tht_rate, dispatch_clock);
-         cprintf(c, "running (%d kb/s)\n",
-                 v * 8 / 1000 / 10);
-         break;
-
-       default:
-         continue;
-       }
-
-       v = avgstat_read(&t->tht_cc_errors, 60, dispatch_clock);
-
-       if(v)
-         cprintf(c, "\t\t%d error%s last minute   %f / second\n",
-                 v, v == 1 ? "" : "s", v / 60.);
-
-       v = avgstat_read_and_expire(&t->tht_cc_errors, dispatch_clock);
-      
-       if(v)
-         cprintf(c, "\t\t%d error%s last hour     %f / second\n",
-                 v, v == 1 ? "" : "s", v / 3600.);
-
-       LIST_FOREACH(s, &t->tht_subscriptions, ths_transport_link) {
-         cprintf(c, "\t\t%s @ prio %d, since %s",
-                 s->ths_title, s->ths_weight, ctime(&s->ths_start));
-         if(s->ths_total_err) {
-           cprintf(c,"\t\t\t%d error%s seen\n",
-                   s->ths_total_err, s->ths_total_err == 1 ? "" : "s");
-         }
-       }
-      }
-      cprintf(c, "\n");
-
-    }
-    return 0;
-  }
-
-  if(!strcasecmp(subcmd, "dvbadapters")) {
-    LIST_FOREACH(tda, &dvb_adapters_running, tda_link) {
-
-      cprintf(c, "%20s:   ", tda->tda_rootpath);
-
-      tdmi = tda->tda_mux_current;
-
-      if(tdmi == NULL) {
-       cprintf(c, "inactive\n");
-       continue;
-      }
-
-      cprintf(c, "Tuned to \"%s\"\n", tdmi->tdmi_shortname);
-      cprintf(c, "\t\t     ");
-
-      print_tdmi(c, tda->tda_mux_current);
-      cprintf(c, "\n");
-    }
-    return 0;
-  }
-
-  if(!strcasecmp(subcmd, "storage")) {
-    cprintf(c, "In-memory storage %lld / %lld\n", 
-           store_mem_size, store_mem_size_max);
-
-    cprintf(c, "  On-disk storage %lld / %lld\n", 
-           store_disk_size, store_disk_size_max);
-
-    cprintf(c, "  %d packets in memory\n",
-           store_packets);
-    return 0;
-  }
-
-  return 1;
-}
-
-
 /*
  *
  */
@@ -697,7 +500,6 @@ const struct {
   const char *name;
   int (*func)(client_t *c, char *argv[], int argc);
 } cr_cmds[] = {
-  { "show", cr_show },
   { "streamport", cr_streamport },
   { "channels.list", cr_channels_list },
   { "channel.info", cr_channel_info },
diff --git a/http.c b/http.c
index f904874fa6992f62a26c52908b60006192ba696c..b1d9d375505d91157ef6d287b6eb87d16a7526c5 100644 (file)
--- a/http.c
+++ b/http.c
@@ -543,7 +543,7 @@ http_con_parse(void *aux, char *buf)
   int n, v;
   char *argv[3], *c;
 
-  // printf("HTTP INPUT: %s\n", buf);
+  //printf("HTTP INPUT: %s\n", buf);
 
   switch(hc->hc_state) {
   case HTTP_CON_WAIT_REQUEST:
index eadf37412a8a76edd03b29a972075dd598c6e499..a43213ad67ac812fd89a29404aab32d397e4aa9b 100644 (file)
@@ -222,12 +222,10 @@ iptv_configure_transport(th_transport_t *t, const char *iptv_type,
           ifname, inet_ntoa(t->tht_iptv_group_addr), t->tht_iptv_port);
   t->tht_name = strdup(buf);
 
-  st = transport_add_stream(t, 0, HTSTV_TABLE);
+  st = transport_add_stream(t, 0, HTSTV_PAT);
   st->st_got_section = iptv_parse_pat;
   st->st_section_docrc = 1;
 
-  t->tht_prio = 50;
-
   s = config_get_str_sub(head, "provider", NULL);
   if(s != NULL)
     t->tht_provider = strdup(s);
@@ -240,12 +238,13 @@ iptv_configure_transport(th_transport_t *t, const char *iptv_type,
   else
     t->tht_network = strdup(inet_ntoa(t->tht_iptv_group_addr));
 
-  snprintf(buf, sizeof(buf), "IPTV:%s:%d",
+  snprintf(buf, sizeof(buf), "iptv_%s_%d",
           inet_ntoa(t->tht_iptv_group_addr), t->tht_iptv_port);
-  t->tht_uniquename = strdup(buf);
+  t->tht_identifier = strdup(buf);
+
+  t->tht_channelname = strdup(channel_name);
 
-  t->tht_channel = channel_find(channel_name, 1, NULL);
-  LIST_INSERT_HEAD(&iptv_probing_transports, t, tht_adapter_link);
+  LIST_INSERT_HEAD(&iptv_probing_transports, t, tht_active_link);
   startupcounter++;
 
   if(!dtimer_isarmed(&iptv_probe_timer)) {
@@ -277,7 +276,7 @@ iptv_probe_done(th_transport_t *t, int timeout)
   LIST_FOREACH(st, &t->tht_streams, st_link)
     pidcnt++;
   
-  LIST_REMOVE(t, tht_adapter_link);
+  LIST_REMOVE(t, tht_active_link);
 
   syslog(LOG_INFO, "iptv: Transport %s probed, %d pids found%s", 
         t->tht_name, pidcnt, timeout ? ", but probe timeouted" : "");
@@ -285,9 +284,9 @@ iptv_probe_done(th_transport_t *t, int timeout)
   iptv_stop_feed(t);
 
   if(!timeout)
-    transport_link(t, t->tht_channel, THT_MPEG_TS);
+    transport_set_channel(t, t->tht_channelname);
   else
-    LIST_INSERT_HEAD(&iptv_stale_transports, t, tht_adapter_link);
+    LIST_INSERT_HEAD(&iptv_stale_transports, t, tht_active_link);
 
   t = LIST_FIRST(&iptv_probing_transports);
   if(t == NULL)
diff --git a/main.c b/main.c
index 8fe50036c5287d3412347e4e0a684725c5a86ef3..e33095ecc08901714e3320625bc58e494da69af0 100644 (file)
--- a/main.c
+++ b/main.c
@@ -183,6 +183,8 @@ main(int argc, char **argv)
   
   settings_dir = config_get_str("settings-dir", NULL);
 
+  settings_dir = NULL;
+
   if(settings_dir == NULL) {
     settings_dir = "/tmp/tvheadend";
     sys_warning = 
@@ -204,6 +206,15 @@ main(int argc, char **argv)
   snprintf(buf, sizeof(buf), "%s/autorec", settings_dir);
   mkdir(buf, 0777);
 
+  snprintf(buf, sizeof(buf), "%s/dvbadapters", settings_dir);
+  mkdir(buf, 0777);
+
+  snprintf(buf, sizeof(buf), "%s/dvbadapters", settings_dir);
+  mkdir(buf, 0777);
+
+  snprintf(buf, sizeof(buf), "%s/dvbmuxes", settings_dir);
+  mkdir(buf, 0777);
+
   syslog(LOG_NOTICE, "Started HTS TV Headend, settings located in \"%s\"",
         settings_dir);
 
@@ -233,7 +244,7 @@ main(int argc, char **argv)
 
   subscriptions_init();
 
-  htmlui_start();
+  //  htmlui_start();
   if(doajax)
     ajaxui_start();
 
@@ -369,26 +380,6 @@ utf8tofilename(const char *in)
 }
 
 
-
-
-
-
-const char *
-htstvstreamtype2txt(tv_streamtype_t s)
-{
-  switch(s) {
-  case HTSTV_MPEG2VIDEO: return "mpeg2video";
-  case HTSTV_MPEG2AUDIO: return "mpeg2audio";
-  case HTSTV_H264:       return "h264";
-  case HTSTV_AC3:        return "AC-3";
-  case HTSTV_TELETEXT:   return "teletext";
-  case HTSTV_SUBTITLES:  return "subtitles";
-  case HTSTV_TABLE:      return "PSI table";
-  default:               return "<unknown>";
-  }
-}
-
-
 FILE *
 settings_open_for_write(const char *name)
 {
diff --git a/psi.c b/psi.c
index d41c91ed2afd0cc5539ef9279c5f5f82d532f359..2f18b8ca31c32db39a41e41b73a3d7195d35b653 100644 (file)
--- a/psi.c
+++ b/psi.c
@@ -23,6 +23,7 @@
 #include <string.h>
 
 #include <libavutil/common.h>
+#include <libavutil/avstring.h>
 
 #include "tvhead.h"
 #include "psi.h"
@@ -90,7 +91,7 @@ psi_parse_pat(th_transport_t *t, uint8_t *ptr, int len,
     pid     = (ptr[2] & 0x1f) << 8 | ptr[3];
 
     if(prognum != 0) {
-      st = transport_add_stream(t, pid, HTSTV_TABLE);
+      st = transport_add_stream(t, pid, HTSTV_PMT);
       st->st_section_docrc = 1;
       st->st_got_section = pmt_callback;
 
@@ -204,7 +205,7 @@ psi_parse_pmt(th_transport_t *t, uint8_t *ptr, int len, int chksvcid)
 
     switch(dtag) {
     case DVB_DESC_CA:
-      st = transport_add_stream(t, (ptr[2] & 0x1f) << 8 | ptr[3], HTSTV_TABLE);
+      st = transport_add_stream(t, (ptr[2] & 0x1f) << 8 | ptr[3], HTSTV_CA);
       st->st_caid = (ptr[0] << 8) | ptr[1];
       break;
 
@@ -507,3 +508,105 @@ psi_caid2name(uint16_t caid)
 {
   return val2str(caid, caidnametab);
 }
+
+/**
+ *
+ */
+static struct strtab streamtypetab[] = {
+  { "MPEG2VIDEO", HTSTV_MPEG2VIDEO },
+  { "MPEG2AUDIO", HTSTV_MPEG2AUDIO },
+  { "H264",       HTSTV_H264 },
+  { "AC3",        HTSTV_AC3 },
+  { "TELETEXT",   HTSTV_TELETEXT },
+  { "SUBTITLES",  HTSTV_SUBTITLES },
+  { "CA",         HTSTV_CA },
+  { "PMT",        HTSTV_PMT },
+  { "PAT",        HTSTV_PAT },
+};
+
+
+
+
+
+
+
+const char *
+htstvstreamtype2txt(tv_streamtype_t s)
+{
+  return val2str(s, streamtypetab) ?: "INVALID";
+}
+
+
+/**
+ * Save transport info
+ */
+void
+psi_save_transport(FILE *fp, th_transport_t *t)
+{
+  th_stream_t *st;
+
+  fprintf(fp, "\tpcr = %d\n", t->tht_pcr_pid);
+
+  LIST_FOREACH(st, &t->tht_streams, st_link) {
+    fprintf(fp, "\tstream {\n");
+    fprintf(fp, "\t\tpid = %d\n", st->st_pid);
+    fprintf(fp, "\t\ttype = %s\n", val2str(st->st_type, streamtypetab) ?: "?");
+    
+    if(st->st_lang[0])
+      fprintf(fp, "\t\tlanguage = %s\n", st->st_lang);
+
+    if(st->st_type == HTSTV_CA)
+      fprintf(fp, "\t\tcaid = %s\n", val2str(st->st_caid, caidnametab) ?: "?");
+
+    if(st->st_frame_duration)
+      fprintf(fp, "\t\tframeduration = %d\n", st->st_frame_duration);
+    
+    fprintf(fp, "\t}\n");
+  }
+}
+
+
+
+/**
+ * Load transport info
+ */
+void
+psi_load_transport(struct config_head *cl, th_transport_t *t)
+{
+  th_stream_t *st;
+  config_entry_t *ce;
+  tv_streamtype_t type;
+  const char *v;
+  int pid;
+
+  t->tht_pcr_pid = atoi(config_get_str_sub(cl, "pcr", "0"));
+  
+  TAILQ_FOREACH(ce, cl, ce_link) {
+    if(ce->ce_type != CFG_SUB || strcasecmp("stream", ce->ce_key))
+      continue;
+
+    type = str2val(config_get_str_sub(&ce->ce_sub, "type", ""), streamtypetab);
+    if(type == -1)
+      continue;
+
+    pid = atoi(config_get_str_sub(&ce->ce_sub, "pid", "0"));
+    if(pid < 1)
+      continue;
+
+    st = transport_add_stream(t, pid, type);
+    st->st_tb = (AVRational){1, 90000};
+    
+    v = config_get_str_sub(&ce->ce_sub, "lang", NULL);
+    if(v != NULL)
+      av_strlcpy(st->st_lang, v, 4);
+
+    st->st_frame_duration =
+      atoi(config_get_str_sub(&ce->ce_sub, "frameduration", "0"));
+     
+    v = config_get_str_sub(&ce->ce_sub, "caid", NULL);
+    if(v != NULL)
+      st->st_caid = str2val(v, caidnametab);
+  }
+}
+
+
diff --git a/psi.h b/psi.h
index 13b6d903a31cb7a3be8ddfc4bdd9d66a5190060a..978fb9f18e45b7887658046f0fddec4c350c2b61 100644 (file)
--- a/psi.h
+++ b/psi.h
@@ -43,4 +43,7 @@ int psi_build_pmt(th_muxer_t *tm, uint8_t *buf0, int maxlen, int pcrpid);
 
 const char *psi_caid2name(uint16_t caid);
 
+void psi_save_transport(FILE *fp, th_transport_t *t);
+void psi_load_transport(struct config_head *cl, th_transport_t *t);
+
 #endif /* PSI_H_ */
index 19eeb847ba84d36e9051de941bbaae7b29177c5f..7c531edbf873b49b6fb4bf36fddbc4134c40b6ba 100644 (file)
@@ -39,6 +39,7 @@
 #include "tvhead.h"
 #include "dispatch.h"
 #include "dvb_dvr.h"
+#include "dvb_support.h"
 #include "teletext.h"
 #include "transports.h"
 #include "subscriptions.h"
 #include "buffer.h"
 #include "channels.h"
 #include "cwc.h"
+#include "strtab.h"
 
-static dtimer_t transport_monitor_timer;
+#define TRANSPORT_HASH_WIDTH 101
 
-static const char *transport_settings_path(th_transport_t *t);
+static struct th_transport_list transporthash[TRANSPORT_HASH_WIDTH];
 
 
+//static dtimer_t transport_monitor_timer;
+
+//static const char *transport_settings_path(th_transport_t *t);
+//static void transport_monitor(void *aux, int64_t now);
+
 void
 transport_stop(th_transport_t *t, int flush_subscriptions)
 {
@@ -73,6 +80,8 @@ transport_stop(th_transport_t *t, int flush_subscriptions)
       return;
   }
 
+  //  dtimer_disarm(&transport_monitor_timer, transport_monitor, t, 1);
+
   t->tht_stop_feed(t);
 
   while((td = LIST_FIRST(&t->tht_descramblers)) != NULL)
@@ -200,12 +209,70 @@ transport_start(th_transport_t *t, unsigned int weight)
 }
 
 
+/**
+ * Return prio for the given transport
+ */
+static int
+transport_get_prio(th_transport_t *t)
+{
+  switch(t->tht_type) {
+  case TRANSPORT_AVGEN:
+  case TRANSPORT_STREAMEDFILE:
+    return 0;
+    
+  case TRANSPORT_DVB:
+    if(t->tht_scrambled)
+      return 3;
+    return 1;
+
+  case TRANSPORT_IPTV:
+    return 2;
+
+  case TRANSPORT_V4L:
+    return 4;
+
+  default:
+    return 5;
+  }
+}
+
+
+/**
+ *  a - b  -> lowest number first
+ */
+static int
+transportcmp(const void *A, const void *B)
+{
+  th_transport_t *a = *(th_transport_t **)A;
+  th_transport_t *b = *(th_transport_t **)B;
+
+  return transport_get_prio(a) - transport_get_prio(b);
+}
 
 
+/**
+ *
+ */
 th_transport_t *
 transport_find(th_channel_t *ch, unsigned int weight)
 {
-  th_transport_t *t;
+  th_transport_t *t, **vec;
+  int cnt = 0, i;
+  
+  /* First, sort all transports in order */
+
+  LIST_FOREACH(t, &ch->ch_transports, tht_channel_link)
+    cnt++;
+
+  vec = alloca(cnt * sizeof(th_transport_t *));
+  i = 0;
+  LIST_FOREACH(t, &ch->ch_transports, tht_channel_link)
+    vec[i++] = t;
+
+  /* Sort transports, lower priority should come come earlier in the vector
+     (i.e. it will be more favoured when selecting a transport */
+
+  qsort(vec, cnt, sizeof(th_transport_t *), transportcmp);
 
   /* First, try all transports without stealing */
 
@@ -251,7 +318,7 @@ transport_compute_weight(struct th_transport_list *head)
   th_subscription_t *s;
   int w = 0;
 
-  LIST_FOREACH(t, head, tht_adapter_link) {
+  LIST_FOREACH(t, head, tht_active_link) {
     LIST_FOREACH(s, &t->tht_subscriptions, ths_transport_link) {
       if(s->ths_weight > w)
        w = s->ths_weight;
@@ -261,7 +328,7 @@ transport_compute_weight(struct th_transport_list *head)
 }
 
 
-
+#if 0
 
 
 static void
@@ -329,35 +396,44 @@ transport_monitor(void *aux, int64_t now)
   }
 }
 
+#endif
+
 
 /**
- *
+ * Create and initialize a new transport struct
  */
-void
-transport_init(th_transport_t *t, int source_type)
+th_transport_t *
+transport_create(const char *identifier, int type, int source_type)
 {
+  unsigned int hash = tvh_strhash(identifier, TRANSPORT_HASH_WIDTH);
+  th_transport_t *t = calloc(1, sizeof(th_transport_t));
+  t->tht_identifier = strdup(identifier);
+  t->tht_type = type;
   t->tht_source_type = source_type;
 
-  avgstat_init(&t->tht_cc_errors, 3600);
-  avgstat_init(&t->tht_rate, 10);
-
-  dtimer_arm(&transport_monitor_timer, transport_monitor, t, 5);
+  LIST_INSERT_HEAD(&transporthash[hash], t, tht_hash_link);
+  return t;
 }
 
-
 /**
- *
+ * Find a transport based on the given identifier
  */
-void
-transport_link(th_transport_t *t, th_channel_t *ch, int source_type)
+th_transport_t *
+transport_find_by_identifier(const char *identifier)
 {
-  transport_set_channel(t, ch);
-  transport_init(t, source_type);
-  LIST_INSERT_HEAD(&all_transports, t, tht_global_link);
+  th_transport_t *t;
+  unsigned int hash = tvh_strhash(identifier, TRANSPORT_HASH_WIDTH);
+  LIST_FOREACH(t, &transporthash[hash], tht_hash_link)
+    if(!strcmp(t->tht_identifier, identifier))
+      break;
+  return t;
 }
 
 
 
+/**
+ * Add a new stream to a transport
+ */
 th_stream_t *
 transport_add_stream(th_transport_t *t, int pid, tv_streamtype_t type)
 {
@@ -390,157 +466,59 @@ transport_add_stream(th_transport_t *t, int pid, tv_streamtype_t type)
 /**
  *
  */
-static int
-transportcmp(th_transport_t *a, th_transport_t *b)
-{
-  return a->tht_prio - b->tht_prio;
-}
-
-
-/**
- *
- */
-int 
-transport_set_channel(th_transport_t *t, th_channel_t *ch)
+void
+transport_set_channel(th_transport_t *t, const char *chname)
 {
-  th_stream_t *st;
-  char *chname;
-  const char *n;
-  char pid[30];
-  char lang[30];
-  struct config_head cl;
-
-  assert(t->tht_uniquename != NULL);
-
-  n = transport_settings_path(t);
-  TAILQ_INIT(&cl);
-  config_read_file0(n, &cl);
-
-  t->tht_prio = atoi(config_get_str_sub(&cl, "prio", "0"));
-  n = config_get_str_sub(&cl, "channel", NULL);
-  if(n != NULL)
-    ch = channel_find(n, 1, NULL); 
+  th_channel_t *ch = channel_find(chname, 1, NULL);
 
-  config_free0(&cl);
+  avgstat_init(&t->tht_cc_errors, 3600);
+  avgstat_init(&t->tht_rate, 10);
 
+  assert(t->tht_identifier != NULL);
   t->tht_channel = ch;
-  LIST_INSERT_SORTED(&ch->ch_transports, t, tht_channel_link, transportcmp);
-
-  chname = utf8toprintable(ch->ch_name);
-
-  syslog(LOG_DEBUG, "Added service \"%s\" for channel \"%s\"",
-        t->tht_name, chname);
-  free(chname);
-
-  LIST_FOREACH(st, &t->tht_streams, st_link) {
-    if(st->st_caid != 0) {
-      n = psi_caid2name(st->st_caid);
-    } else {
-      n = htstvstreamtype2txt(st->st_type);
-    }
-    if(st->st_pid < 8192) {
-      snprintf(pid, sizeof(pid), " on pid [%d]", st->st_pid);
-    } else {
-      pid[0] = 0;
-    }
-
-    if(st->st_lang[0]) {
-      snprintf(lang, sizeof(lang), ", language \"%s\"", st->st_lang);
-    } else {
-      lang[0] = 0;
-    }
-
-    syslog(LOG_DEBUG, "   Stream \"%s\"%s%s", n, lang, pid);
-  }
 
-  return 0;
+  LIST_INSERT_HEAD(&ch->ch_transports, t, tht_channel_link);
 }
 
-
 /**
  *
  */
 void
-transport_set_priority(th_transport_t *t, int prio)
+transport_unset_channel(th_transport_t *t)
 {
-  th_channel_t *ch = t->tht_channel;
-
-  if(t->tht_prio == prio)
-    return;
-
+  t->tht_channel = NULL;
   LIST_REMOVE(t, tht_channel_link);
-  t->tht_prio = prio;
-  LIST_INSERT_SORTED(&ch->ch_transports, t, tht_channel_link, transportcmp);
-  transport_settings_write(t);
 }
 
 
+
 /**
  *
  */
-void
-transport_move(th_transport_t *t, th_channel_t *ch)
-{
-  LIST_REMOVE(t, tht_channel_link);
-  t->tht_channel = ch;
-  LIST_INSERT_SORTED(&ch->ch_transports, t, tht_channel_link, transportcmp);
-  transport_settings_write(t);
-}
 
+static struct strtab stypetab[] = {
+  { "SDTV",         ST_SDTV },
+  { "Radio",        ST_RADIO },
+  { "HDTV",         ST_HDTV },
+  { "SDTV-AC",      ST_AC_SDTV },
+  { "HDTV-AC",      ST_AC_HDTV },
+};
 
-/**
- * Generate a settings-dir relative path to transport config
- *
- * Must be free'd by caller
- */
-static const char *
-transport_settings_path(th_transport_t *t)
+const char *
+transport_servicetype_txt(th_transport_t *t)
 {
-  char buf[400];
-  char buf2[400];
-
-  int l, i;
-  char c;
-
-  l = strlen(t->tht_uniquename);
-  if(l >= sizeof(buf2))
-      l = sizeof(buf2) - 1;
-
-  for(i = 0; i < l; i++) {
-    c = tolower(t->tht_uniquename[i]);
-    if(isalnum(c))
-      buf2[i] = c;
-    else
-      buf2[i] = '_';
-  }
-
-  buf2[i] = 0;
-  snprintf(buf, sizeof(buf), "%s/transports/%s", settings_dir, buf2);
-  return strdup(buf);
+  return val2str(t->tht_servicetype, stypetab);
 }
 
-
 /**
- * Write out a config file for a channel
+ *
  */
-void
-transport_settings_write(th_transport_t *t)
+int
+transport_is_tv(th_transport_t *t)
 {
-  FILE *fp;
-  const char *n;
-
-  n = transport_settings_path(t);
-    
-  if((fp = settings_open_for_write(n)) != NULL) {
-    fprintf(fp, 
-           "uniquename = %s\n"
-           "channel = %s\n"
-           "prio = %d\n",
-           t->tht_uniquename,
-           t->tht_channel->ch_name,
-           t->tht_prio);
-    fclose(fp);
-  }
-  free((void *)n);
+  return 
+    t->tht_servicetype == ST_SDTV    ||
+    t->tht_servicetype == ST_HDTV    ||
+    t->tht_servicetype == ST_AC_SDTV ||
+    t->tht_servicetype == ST_AC_HDTV;
 }
-
index 3d3f4ea9f2c9cdfb556221a4987f14238221e0f6..64e0a496a3575f095d582099ee68e1699fa9de4b 100644 (file)
@@ -25,11 +25,14 @@ unsigned int transport_compute_weight(struct th_transport_list *head);
 
 void transport_stop(th_transport_t *t, int flush_subscriptions);
 
-void transport_init(th_transport_t *t, int source_type);
+th_transport_t *transport_create(const char *identifier, int type,
+                                int source_type);
 
-void transport_link(th_transport_t *t, th_channel_t *ch, int source_type);
+th_transport_t *transport_find_by_identifier(const char *identifier);
 
-int transport_set_channel(th_transport_t *t, th_channel_t *ch);
+void transport_set_channel(th_transport_t *t, const char *name);
+
+void transport_unset_channel(th_transport_t *t);
 
 th_transport_t *transport_find(th_channel_t *ch, unsigned int weight);
 
@@ -38,8 +41,10 @@ th_stream_t *transport_add_stream(th_transport_t *t, int pid,
 
 void transport_set_priority(th_transport_t *t, int prio);
 
-void transport_move(th_transport_t *t, th_channel_t *ch);
-
 void transport_settings_write(th_transport_t *t);
 
+const char *transport_servicetype_txt(th_transport_t *t);
+
+int transport_is_tv(th_transport_t *t);
+
 #endif /* TRANSPORTS_H */
index cfc04aaa354a05ee8a82e35300f60953571303ba..3563756d262a0d26db9fb2175dee29b1fae1512b 100644 (file)
--- a/tsdemux.c
+++ b/tsdemux.c
@@ -57,13 +57,14 @@ got_section(th_transport_t *t, th_stream_t *st)
 {
   th_descrambler_t *td;
 
-  LIST_FOREACH(td, &t->tht_descramblers, td_transport_link)
-    td->td_table(td, t, st, 
-                st->st_section->ps_data, st->st_section->ps_offset);
-
-  if(st->st_got_section != NULL)
+  if(st->st_type == HTSTV_CA) {
+    LIST_FOREACH(td, &t->tht_descramblers, td_transport_link)
+      td->td_table(td, t, st, 
+                  st->st_section->ps_data, st->st_section->ps_offset);
+  } else if(st->st_got_section != NULL) {
     st->st_got_section(t, st, st->st_section->ps_data,
                       st->st_section->ps_offset);
+  }
 }
 
 
@@ -100,7 +101,9 @@ ts_recv_packet0(th_transport_t *t, th_stream_t *st, uint8_t *tsb)
 
   switch(st->st_type) {
 
-  case HTSTV_TABLE:
+  case HTSTV_CA:
+  case HTSTV_PAT:
+  case HTSTV_PMT:
     if(st->st_section == NULL)
       st->st_section = calloc(1, sizeof(struct psi_section));
 
index 7534c4cf7f49e558f4b35b1bdb378b2b0e33fdb1..cf023fb72335975cd9a5a7584ea5761af1282136 100644 (file)
--- a/tvhead.h
+++ b/tvhead.h
@@ -136,17 +136,16 @@ typedef struct th_v4l_adapter {
  * Mux instance modes
  */
 typedef enum {
-    TDMI_CONFIGURED,
-    TDMI_INITIAL_SCAN,
-    TDMI_IDLE,
-    TDMI_RUNNING,
-    TDMI_IDLESCAN,
+  TDMI_IDLE,         /* Not tuned */
+  TDMI_RUNNING,      /* Tuned with a subscriber */
+  TDMI_IDLESCAN,     /* Just scanning */
 } tdmi_state_t;
 
 /*
  * DVB Mux instance
  */
 typedef struct th_dvb_mux_instance {
+  LIST_ENTRY(th_dvb_mux_instance) tdmi_global_link;
   LIST_ENTRY(th_dvb_mux_instance) tdmi_adapter_link;
 
   struct th_dvb_adapter *tdmi_adapter;
@@ -171,15 +170,16 @@ typedef struct th_dvb_mux_instance {
   time_t tdmi_got_adapter;
   time_t tdmi_lost_adapter;
 
-  int tdmi_type;                           /* really fe_type_t */
   struct dvb_frontend_parameters *tdmi_fe_params;
-  int tdmi_polarisation;
+  uint8_t tdmi_polarisation;  /* for DVB-S */
+  uint8_t tdmi_switchport;    /* for DVB-S */
 
-  const char *tdmi_shortname;   /* Only unique for the specific adapter */
-  const char *tdmi_uniquename;  /* Globally unique */
+  char *tdmi_identifier;
   const char *tdmi_network;     /* Name of network, from NIT table */
-} th_dvb_mux_instance_t;
 
+  struct th_transport_list tdmi_transports; /* via tht_mux_link */
+
+} th_dvb_mux_instance_t;
 
 
 /*
@@ -190,10 +190,9 @@ typedef struct th_dvb_table {
   char *tdt_name;
   void *tdt_handle;
   struct th_dvb_mux_instance *tdt_tdmi;
-  int tdt_count; /* times seen */
   void *tdt_opaque;
-  int (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
-                     uint8_t tableid, void *opaque);
+  void (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
+                      uint8_t tableid, void *opaque);
 
   int tdt_fd;
   struct dmx_sct_filter_params *tdt_fparams;
@@ -205,20 +204,21 @@ typedef struct th_dvb_table {
  */
 typedef struct th_dvb_adapter {
 
-  struct th_dvb_mux_instance_list tda_muxes_configured;
+  LIST_ENTRY(th_dvb_adapter) tda_global_link;
 
-  struct th_dvb_mux_instance_list tda_muxes_active;
+  enum {
+    TDA_STATE_UNCONFIGURED,    /* Found but not configured */
+    TDA_STATE_RUNNING,         /* Configured */
+    TDA_STATE_ZOMBIE,          /* Configured but not found */
+  } tda_state;
+
+  struct th_dvb_mux_instance_list tda_muxes;
   th_dvb_mux_instance_t *tda_mux_current;
 
   const char *tda_rootpath;
-  const char *tda_info;
-  const char *tda_sname;
-
-  LIST_ENTRY(th_dvb_adapter) tda_link;
-
-  LIST_HEAD(, th_dvb_mux) tda_muxes;
+  char *tda_identifier;
 
-  int tda_running;
+  char *tda_displayname;
 
   pthread_mutex_t tda_lock;
   pthread_cond_t tda_cond;
@@ -365,7 +365,7 @@ typedef struct th_transport {
   
   const char *tht_name;
 
-  LIST_ENTRY(th_transport) tht_global_link;
+  LIST_ENTRY(th_transport) tht_hash_link;
 
   enum {
     TRANSPORT_DVB,
@@ -385,7 +385,6 @@ typedef struct th_transport {
 
   int tht_tt_rundown_content_length;
   time_t tht_tt_clock;   /* Network clock as determined by teletext decoder */
-  int tht_prio;
   
   struct th_stream_list tht_streams;
   th_stream_t *tht_video;
@@ -393,8 +392,9 @@ typedef struct th_transport {
   
   int64_t  tht_pcr_drift;
   uint16_t tht_pcr_pid;
-  uint16_t tht_dvb_transport_id;
   uint16_t tht_dvb_service_id;
+  uint16_t tht_pmt;
+
   int tht_pmt_seen;
 
   avgstat_t tht_cc_errors;
@@ -406,7 +406,11 @@ typedef struct th_transport {
 
   int64_t tht_dts_start;
   
-  LIST_ENTRY(th_transport) tht_adapter_link;
+  LIST_ENTRY(th_transport) tht_mux_link;
+
+  LIST_ENTRY(th_transport) tht_tmp_link;
+
+  LIST_ENTRY(th_transport) tht_active_link;
 
   LIST_ENTRY(th_transport) tht_channel_link;
   struct th_channel *tht_channel;
@@ -418,6 +422,7 @@ typedef struct th_transport {
 
   void (*tht_stop_feed)(struct th_transport *t);
 
+  void (*tht_config_change)(struct th_transport *t);
 
   struct th_muxer_list tht_muxers; /* muxers */
 
@@ -460,9 +465,20 @@ typedef struct th_transport {
     } file_input;
  } u;
 
+  const char *tht_identifier;
+  const char *tht_servicename;
+  const char *tht_channelname;
   const char *tht_provider;
-  const char *tht_uniquename;
   const char *tht_network;
+  enum {
+    /* Service types defined in EN 300 468 */
+
+    ST_SDTV       = 0x1,    /* SDTV (MPEG2) */
+    ST_RADIO      = 0x2,
+    ST_HDTV       = 0x11,   /* HDTV (MPEG2) */
+    ST_AC_SDTV    = 0x16,   /* Advanced codec SDTV */
+    ST_AC_HDTV    = 0x19,   /* Advanced codec HDTV */
+  } tht_servicetype;
 
   enum {
     THT_MPEG_TS,
@@ -866,4 +882,13 @@ extern th_channel_group_t *defgroup;
 struct config_head *user_resolve_to_config(const char *username, 
                                           const char *password);
 
+extern inline unsigned int tvh_strhash(const char *s, unsigned int mod)
+{
+  unsigned int v = 5381;
+  while(*s)
+    v += (v << 5) + v + *s++;
+  return v % mod;
+}
+
+
 #endif /* TV_HEAD_H */
diff --git a/v4l.c b/v4l.c
index 0678aded3428eba03438518cdf50d4ae4b76c58b..0650df927539b9869dcaf1a058c3ed986a629fb2 100644 (file)
--- a/v4l.c
+++ b/v4l.c
@@ -95,10 +95,10 @@ v4l_configure_transport(th_transport_t *t, const char *muxname,
 
   t->tht_network = strdup("Analog TV");
   t->tht_provider = strdup("Analog TV");
-  snprintf(buf, sizeof(buf), "ANALOG:%u", t->tht_v4l_frequency);
-  t->tht_uniquename = strdup(buf);
+  snprintf(buf, sizeof(buf), "analog_%u", t->tht_v4l_frequency);
+  t->tht_identifier = strdup(buf);
 
-  transport_link(t, channel_find(channel_name, 1, NULL), THT_OTHER);
+  transport_set_channel(t, channel_name);
   return 0;
 }
 
@@ -199,7 +199,7 @@ v4l_stop_feed(th_transport_t *t)
   th_v4l_adapter_t *tva = t->tht_v4l_adapter;
 
   t->tht_v4l_adapter = NULL;
-  LIST_REMOVE(t, tht_adapter_link);
+  LIST_REMOVE(t, tht_active_link);
 
   t->tht_status = TRANSPORT_IDLE;
 
@@ -268,7 +268,7 @@ v4l_start_feed(th_transport_t *t, unsigned int weight, int status)
   if(v4l_setfreq(tva, tva->tva_frequency))
     return 1;
 
-  LIST_INSERT_HEAD(&tva->tva_transports, t, tht_adapter_link);
+  LIST_INSERT_HEAD(&tva->tva_transports, t, tht_active_link);
   t->tht_v4l_adapter = tva;
   t->tht_status = TRANSPORT_RUNNING;