+
+#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, "%s ", 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, "Status:
"
+ "%s
",
+ val2str(tda->tda_state, adapterstatus) ?: "invalid");
+ tcp_qprintf(&tq, "Type:
"
+ "%s
",
+ dvb_adaptertype_to_str(tda->tda_fe_info->type));
+
+ tcp_qprintf(&tq, "Tuner:
"
+ "...
");
+ tcp_qprintf(&tq, "", 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, "");
+
+ LIST_FOREACH(tda, &dvb_adapters, tda_global_link) {
+
+ tcp_qprintf(&tq, "
",
+ 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, "
");
+ tcp_qprintf(&tq, "
");
+ 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,
+ "Add new...
", tda->tda_identifier);
+
+ if(result) {
+ tcp_qprintf(tq, "%s
", 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,
+ "%s
",
+ tda->tda_displayname);
+
+ ajax_box_end(&tq, AJAX_BOX_FILLED);
+
+ /* Type */
+
+ tcp_qprintf(&tq, "Model:
"
+ "%s (%s)
",
+ 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, "Freq. Range:
"
+ "%.2f - %.2f kHz, in steps of %.2f kHz
",
+ a, b, c);
+
+
+ if(tda->tda_fe_info->symbol_rate_min) {
+ tcp_qprintf(&tq, "Symbolrate:
"
+ "%d - %d BAUD
",
+ tda->tda_fe_info->symbol_rate_min,
+ tda->tda_fe_info->symbol_rate_max);
+ }
+
+
+ /* Capabilities */
+
+
+ // tcp_qprintf(&tq, "Capabilities:
");
+
+ tcp_qprintf(&tq, "");
+ ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, NULL, NULL, "Multiplexes");
+
+ /* List of muxes */
+
+ tcp_qprintf(&tq, "
");
+
+ tcp_qprintf(&tq, "
"
+ "Freq.
");
+ tcp_qprintf(&tq, "
%s
",
+ "Status");
+ tcp_qprintf(&tq, "
%s
",
+ "State");
+ tcp_qprintf(&tq, "
%s
",
+ "Name");
+ tcp_qprintf(&tq, "
%s
",
+ "Services");
+ tcp_qprintf(&tq, "
");
+
+
+ tcp_qprintf(&tq, "
",
+ tda->tda_identifier);
+ tcp_qprintf(&tq, "
");
+
+
+
+ ajax_js(&tq,
+ "new Ajax.PeriodicalUpdater('dvbmuxlist%s', "
+ "'/ajax/dvbadaptermuxlist/%s', {method: 'get', frequency: 5}) ",
+ tda->tda_identifier, tda->tda_identifier);
+
+ tcp_qprintf(&tq, "
");
+ dvb_make_add_link(&tq, tda, NULL);
+ tcp_qprintf(&tq, "
");
+
+ ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
+ tcp_qprintf(&tq, "
");
+
+ /* Div for displaying services */
+
+ tcp_qprintf(&tq, "");
+ tcp_qprintf(&tq, "
");
+
+ 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, ""
+ "Add new %s mux
",
+ dvb_adaptertype_to_str(tda->tda_fe_info->type));
+
+ tcp_qprintf(&tq,
+ "Frequency (%s):
"
+ ""
+ " "
+ "
",
+ fetype == FE_QPSK ? "kHz" : "Hz");
+
+ snprintf(params, sizeof(params),
+ "freq: $F('freq')");
+
+
+ /* Symbolrate */
+
+ if(fetype == FE_QAM || fetype == FE_QPSK) {
+
+ tcp_qprintf(&tq,
+ "Symbolrate:
"
+ ""
+ " "
+ "
");
+
+ snprintf(params + strlen(params), sizeof(params) - strlen(params),
+ ", symrate: $F('symrate')");
+ }
+
+ /* Bandwidth */
+
+ if(fetype == FE_OFDM) {
+ tcp_qprintf(&tq,
+ "Bandwidth:
"
+ "");
+
+ 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, "
");
+
+ snprintf(params + strlen(params), sizeof(params) - strlen(params),
+ ", bw: $F('bw')");
+ }
+
+
+ /* Constellation */
+
+ if(fetype == FE_QAM || fetype == FE_OFDM) {
+ tcp_qprintf(&tq,
+ "Constellation:
"
+ "");
+
+ 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, "
");
+
+ snprintf(params + strlen(params), sizeof(params) - strlen(params),
+ ", const: $F('const')");
+ }
+
+
+ /* FEC */
+
+ if(fetype == FE_QAM || fetype == FE_QPSK) {
+ tcp_qprintf(&tq,
+ "FEC:
"
+ "");
+
+ 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, "
");
+
+ snprintf(params + strlen(params), sizeof(params) - strlen(params),
+ ", fec: $F('fec')");
+ }
+
+
+ if(fetype == FE_OFDM) {
+ tcp_qprintf(&tq,
+ "Transmission mode:
"
+ "");
+
+ add_option(&tq, caps & FE_CAN_TRANSMISSION_MODE_AUTO, "AUTO");
+ add_option(&tq, 1 , "2k");
+ add_option(&tq, 1 , "8k");
+ tcp_qprintf(&tq, "
");
+
+ snprintf(params + strlen(params), sizeof(params) - strlen(params),
+ ", tmode: $F('tmode')");
+
+ tcp_qprintf(&tq,
+ "Guard interval:
"
+ "");
+
+ 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, "
");
+
+ snprintf(params + strlen(params), sizeof(params) - strlen(params),
+ ", guard: $F('guard')");
+
+
+
+ tcp_qprintf(&tq,
+ "Hierarchy:
"
+ "");
+
+ 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, "
");
+
+
+ snprintf(params + strlen(params), sizeof(params) - strlen(params),
+ ", hier: $F('hier')");
+
+
+
+ tcp_qprintf(&tq,
+ "FEC Hi:
"
+ "");
+
+ 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, "
");
+
+ snprintf(params + strlen(params), sizeof(params) - strlen(params),
+ ", fechi: $F('fechi')");
+
+
+ tcp_qprintf(&tq,
+ "FEC Low:
"
+ "");
+
+ 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, "
");
+
+ snprintf(params + strlen(params), sizeof(params) - strlen(params),
+ ", feclo: $F('feclo')");
+ }
+
+ tcp_qprintf(&tq,
+ ""
+ " "
+ "
", 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, ""
+ "No muxes configured
");
+ } else LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
+
+ tcp_qprintf(&tq, "", o ? " style=\"background: #fff\"" : "");
+ o = !o;
+
+ tdmi_displayname(tdmi, buf, sizeof(buf));
+
+ tcp_qprintf(&tq, "
");
+ tcp_qprintf(&tq, "
", tdmi->tdmi_identifier, buf);
+
+ tcp_qprintf(&tq, "
%s
",
+ 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, "
%s
",
+ txt);
+
+ txt = tdmi->tdmi_network;
+ if(txt == NULL)
+ txt = "Unknown";
+
+ tcp_qprintf(&tq, "
%s
",
+ txt);
+
+ n = 0;
+ LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link)
+ n++;
+
+ tcp_qprintf(&tq, "
%d
", n);
+
+ tcp_qprintf(&tq, "
");
+ }
+
+ 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
index 00000000..32f7411b
--- /dev/null
+++ b/ajaxui/ajaxui_config_transport.c
@@ -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 .
+ */
+
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#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, "\r\n");
+
+ /* Top */
+
+ tcp_qprintf(tq, "");
+ 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='"
+ "%s "
+ "';\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
index 00000000..a4494940
Binary files /dev/null and b/ajaxui/images/mapped.png differ
diff --git a/ajaxui/images/sbbody_l.gif b/ajaxui/images/sbbody_l.gif
index 12806b12..a7adf2d3 100644
Binary files a/ajaxui/images/sbbody_l.gif and b/ajaxui/images/sbbody_l.gif differ
diff --git a/ajaxui/images/sbbody_r.gif b/ajaxui/images/sbbody_r.gif
index 2e6cac7d..51b09101 100644
Binary files a/ajaxui/images/sbbody_r.gif and b/ajaxui/images/sbbody_r.gif differ
diff --git a/ajaxui/images/sbhead_r.gif b/ajaxui/images/sbhead_r.gif
index 9d7d4a1e..c6eb7fdb 100644
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
index 00000000..0f2f7388
Binary files /dev/null and b/ajaxui/images/unmapped.png differ
diff --git a/ajaxui/prototype/prototype.js b/ajaxui/prototype/prototype.js
index e02e94da..33ec9d9e 100644
--- a/ajaxui/prototype/prototype.js
+++ b/ajaxui/prototype/prototype.js
@@ -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) {
diff --git a/ajaxui/tvheadend.js b/ajaxui/tvheadend.js
index eeb46862..306c5f7e 100644
--- a/ajaxui/tvheadend.js
+++ b/ajaxui/tvheadend.js
@@ -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 98005d04..e9879e9c 100644
--- 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);
}
diff --git a/channels.c b/channels.c
index db6bb520..b2b56f2b 100644
--- a/channels.c
+++ b/channels.c
@@ -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 8a30ace2..8655a012 100644
--- 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 3c47985c..1b7c724e 100644
Binary files a/dvb.c and b/dvb.c differ
diff --git a/dvb.h b/dvb.h
index 72946499..4e01653d 100644
--- 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_ */
diff --git a/dvb_dvr.c b/dvb_dvr.c
index c07e15bb..dd5baff9 100644
--- 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);
diff --git a/dvb_fe.c b/dvb_fe.c
index 1d11f9f6..639e8eee 100644
--- 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 */
diff --git a/dvb_muxconfig.c b/dvb_muxconfig.c
index 28451878..4cf7f9f3 100644
--- a/dvb_muxconfig.c
+++ b/dvb_muxconfig.c
@@ -30,86 +30,10 @@
#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
diff --git a/dvb_muxconfig.h b/dvb_muxconfig.h
index 64684dc7..dcdd20fc 100644
--- a/dvb_muxconfig.h
+++ b/dvb_muxconfig.h
@@ -19,6 +19,21 @@
#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 */
diff --git a/dvb_support.c b/dvb_support.c
index 80bf523e..3611e3da 100644
--- a/dvb_support.c
+++ b/dvb_support.c
@@ -29,8 +29,12 @@
#include
#include
+#include
+
#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;
+}
diff --git a/dvb_support.h b/dvb_support.h
index 2cef686b..3d0c3ade 100644
--- a/dvb_support.h
+++ b/dvb_support.h
@@ -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
@@ -41,15 +42,6 @@
#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 */
diff --git a/dvb_tables.c b/dvb_tables.c
index 383cd622..46a2ab20 100644
--- a/dvb_tables.c
+++ b/dvb_tables.c
@@ -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--;
}
+ t = dvb_find_transport(tdmi, service_id, 0);
+ if(t == NULL)
+ break;
+
+ change |=
+ t->tht_servicetype != stype ||
+ t->tht_scrambled != free_ca_mode ||
+ strcmp(t->tht_provider ?: "", provider) ||
+ strcmp(t->tht_servicename ?: "", chname );
+
+ t->tht_servicetype = stype;
+ t->tht_scrambled = free_ca_mode;
+
+ free((void *)t->tht_provider);
+ t->tht_provider = strdup(provider);
+
+ free((void *)t->tht_servicename);
+ t->tht_servicename = strdup(chname);
+
+ if(t->tht_channelname == NULL)
+ t->tht_channelname = strdup(chname);
}
break;
}
len -= dlen; ptr += dlen; dllen -= dlen;
}
-
-
- if(chname != NULL) switch(stype) {
-
- case DVB_ST_SDTV:
- case DVB_ST_HDTV:
- case DVB_ST_AC_SDTV:
- case DVB_ST_AC_HDTV:
- /* TV service */
-
- 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) */
- }
- }
- }
- break;
-
- }
}
- 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);
}
diff --git a/file_input.c b/file_input.c
index e3f231d1..30e4d315 100644
--- a/file_input.c
+++ b/file_input.c
@@ -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);
}
}
diff --git a/htsclient.c b/htsclient.c
index 95a070ec..f240c658 100644
--- a/htsclient.c
+++ b/htsclient.c
@@ -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 ?: "");
-
- 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 f904874f..b1d9d375 100644
--- 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:
diff --git a/iptv_input.c b/iptv_input.c
index eadf3741..a43213ad 100644
--- a/iptv_input.c
+++ b/iptv_input.c
@@ -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_channel = channel_find(channel_name, 1, NULL);
- LIST_INSERT_HEAD(&iptv_probing_transports, t, tht_adapter_link);
+ t->tht_channelname = strdup(channel_name);
+
+ 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 8fe50036..e33095ec 100644
--- 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 "";
- }
-}
-
-
FILE *
settings_open_for_write(const char *name)
{
diff --git a/psi.c b/psi.c
index d41c91ed..2f18b8ca 100644
--- a/psi.c
+++ b/psi.c
@@ -23,6 +23,7 @@
#include
#include
+#include
#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 13b6d903..978fb9f1 100644
--- 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_ */
diff --git a/transports.c b/transports.c
index 19eeb847..7c531edb 100644
--- a/transports.c
+++ b/transports.c
@@ -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"
@@ -51,12 +52,18 @@
#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
+ */
+th_transport_t *
+transport_find_by_identifier(const char *identifier)
+{
+ 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
*/
-void
-transport_link(th_transport_t *t, th_channel_t *ch, int source_type)
-{
- transport_set_channel(t, ch);
- transport_init(t, source_type);
- LIST_INSERT_HEAD(&all_transports, t, tht_global_link);
-}
-
-
-
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)
+void
+transport_set_channel(th_transport_t *t, const char *chname)
{
- return a->tht_prio - b->tht_prio;
-}
+ th_channel_t *ch = channel_find(chname, 1, NULL);
+ avgstat_init(&t->tht_cc_errors, 3600);
+ avgstat_init(&t->tht_rate, 10);
-/**
- *
- */
-int
-transport_set_channel(th_transport_t *t, th_channel_t *ch)
-{
- 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);
-
- config_free0(&cl);
-
+ 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)
+
+static struct strtab stypetab[] = {
+ { "SDTV", ST_SDTV },
+ { "Radio", ST_RADIO },
+ { "HDTV", ST_HDTV },
+ { "SDTV-AC", ST_AC_SDTV },
+ { "HDTV-AC", ST_AC_HDTV },
+};
+
+const char *
+transport_servicetype_txt(th_transport_t *t)
{
- 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);
+ return val2str(t->tht_servicetype, stypetab);
}
-
/**
- * 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)
+int
+transport_is_tv(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
+ t->tht_servicetype == ST_SDTV ||
+ t->tht_servicetype == ST_HDTV ||
+ t->tht_servicetype == ST_AC_SDTV ||
+ t->tht_servicetype == ST_AC_HDTV;
}
-
-
-/**
- * Write out a config file for a channel
- */
-void
-transport_settings_write(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);
-}
-
diff --git a/transports.h b/transports.h
index 3d3f4ea9..64e0a496 100644
--- a/transports.h
+++ b/transports.h
@@ -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 */
diff --git a/tsdemux.c b/tsdemux.c
index cfc04aaa..3563756d 100644
--- 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));
diff --git a/tvhead.h b/tvhead.h
index 7534c4cf..cf023fb7 100644
--- 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;
+ char *tda_identifier;
- LIST_ENTRY(th_dvb_adapter) tda_link;
-
- LIST_HEAD(, th_dvb_mux) tda_muxes;
-
- 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 0678aded..0650df92 100644
--- 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;