/* * 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 "dvb.h" #include "dvb_support.h" #include "dvb_muxconfig.h" #include "psi.h" #include "transports.h" #include "ajaxui_mailbox.h" #if 0 static struct strtab adapterstatus[] = { { "Hardware detected", TDA_STATE_RUNNING }, { "Hardware not found", TDA_STATE_ZOMBIE }, }; #endif static void add_option(tcp_queue_t *tq, int bol, const char *name) { if(bol) tcp_qprintf(tq, "", 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_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, http_reply_t *hr, const char *remain, void *opaque) { tcp_queue_t *tq = &hr->hr_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; 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, "
Device:
" "
%s
", tda->tda_rootpath ?: "Not present"); tcp_qprintf(tq, "
Type:
" "
%s
", dvb_adaptertype_to_str(tda->tda_type)); tcp_qprintf(tq, "
" "Edit
", tda->tda_identifier); ajax_box_end(tq, AJAX_BOX_SIDEBOX); http_output_html(hc, hr); return 0; } /** * DVB configuration */ int ajax_config_dvb_tab(http_connection_t *hc, http_reply_t *hr) { tcp_queue_t *tq = &hr->hr_tq; th_dvb_adapter_t *tda; tcp_qprintf(tq, "
"); TAILQ_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_html(hc, hr); return 0; } /** * Generate the 'add new...' mux link if the adapter has backing hardware * * 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) { if(tda->tda_fe_info != NULL) { 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, http_reply_t *hr, const char *remain, void *opaque) { tcp_queue_t *tq = &hr->hr_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; 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 ? tda->tda_fe_info->name : "", dvb_adaptertype_to_str(tda->tda_type)); if(tda->tda_fe_info != NULL) { switch(tda->tda_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"); tcp_qprintf(tq, "
", tda->tda_identifier); ajax_js(tq, "new Ajax.Updater('dvbmuxlist_%s', " "'/ajax/dvbadaptermuxlist/%s', {method: 'get', evalScripts: true})", 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_html(hc, hr); return 0; } /** * DVB adapter add mux */ static int ajax_adapteraddmux(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { tcp_queue_t *tq = &hr->hr_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; if(tda->tda_fe_info == NULL) return HTTP_STATUS_BAD_REQUEST; caps = tda->tda_fe_info->caps; fetype = tda->tda_fe_info->type; 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:
" "
"); snprintf(params + strlen(params), sizeof(params) - strlen(params), ", bw: $F('bw')"); } /* Constellation */ if(fetype == FE_QAM || fetype == FE_OFDM) { tcp_qprintf(tq, "
Constellation:
" "
"); snprintf(params + strlen(params), sizeof(params) - strlen(params), ", const: $F('const')"); } /* FEC */ if(fetype == FE_QAM || fetype == FE_QPSK) { tcp_qprintf(tq, "
FEC:
" "
"); snprintf(params + strlen(params), sizeof(params) - strlen(params), ", fec: $F('fec')"); } if(fetype == FE_QPSK) { tcp_qprintf(tq, "
Polarisation:
" "
"); snprintf(params + strlen(params), sizeof(params) - strlen(params), ", pol: $F('pol')"); } if(fetype == FE_OFDM) { tcp_qprintf(tq, "
Transmission mode:
" "
"); snprintf(params + strlen(params), sizeof(params) - strlen(params), ", tmode: $F('tmode')"); tcp_qprintf(tq, "
Guard interval:
" "
"); snprintf(params + strlen(params), sizeof(params) - strlen(params), ", guard: $F('guard')"); tcp_qprintf(tq, "
Hierarchy:
" "
"); snprintf(params + strlen(params), sizeof(params) - strlen(params), ", hier: $F('hier')"); tcp_qprintf(tq, "
FEC Hi:
" "
"); snprintf(params + strlen(params), sizeof(params) - strlen(params), ", fechi: $F('fechi')"); tcp_qprintf(tq, "
FEC Low:
" "
"); snprintf(params + strlen(params), sizeof(params) - strlen(params), ", feclo: $F('feclo')"); } tcp_qprintf(tq, "
" "" "
", tda->tda_identifier, params); http_output_html(hc, hr); return 0; } /** * */ static int ajax_adaptercreatemux_fail(http_connection_t *hc, http_reply_t *hr, th_dvb_adapter_t *tda, const char *errmsg) { tcp_queue_t *tq = &hr->hr_tq; dvb_make_add_link(tq, tda, errmsg); http_output_html(hc, hr); return 0; } /** * DVB adapter create mux (come here on POST after addmux query) */ static int ajax_adaptercreatemux(http_connection_t *hc, http_reply_t *hr, 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, "65535", NULL, 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, hr, tda, v); tq = &hr->hr_tq; dvb_make_add_link(tq, tda, "Successfully created"); ajax_js(tq, "new Ajax.Updater('dvbmuxlist_%s', " "'/ajax/dvbadaptermuxlist/%s', {method: 'get', evalScripts: true})", tda->tda_identifier, tda->tda_identifier); http_output_html(hc, hr); return 0; } /** * Return a list of all muxes on the given adapter */ static int ajax_adaptermuxlist(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { th_dvb_mux_instance_t *tdmi; tcp_queue_t *tq = &hr->hr_tq; th_dvb_adapter_t *tda; char buf[50], buf2[500], buf3[20]; const char *txt; int fetype, n, m; th_transport_t *t; int o = 1, v; int csize[10]; int nmuxes = 0; int displines = 21; const char *cells[10]; if(remain == NULL || (tda = dvb_adapter_find_by_identifier(remain)) == NULL) return HTTP_STATUS_NOT_FOUND; fetype = tda->tda_type; /* List of muxes */ LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) nmuxes++; ajax_table_header(hc, tq, (const char *[]) {"Freq", "Status", "State", "Name", "Services", NULL}, (int[]){4,3,2,4,2}, nmuxes > displines, csize); tcp_qprintf(tq, "
"); v = displines; if(nmuxes < displines) v = nmuxes; tcp_qprintf(tq, "
", tda->tda_identifier, v * 14); if(nmuxes == 0) { tcp_qprintf(tq, "
" "No muxes configured
"); } else LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) { tdmi_displayname(tdmi, buf, sizeof(buf)); snprintf(buf2, sizeof(buf2), "%s", tdmi->tdmi_identifier, buf); cells[0] = buf2; cells[1] = 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; } cells[2] = txt; txt = tdmi->tdmi_network; if(txt == NULL) txt = "Unknown"; cells[3] = txt; n = m = 0; LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) { n++; if(transport_is_available(t)) m++; } snprintf(buf3, sizeof(buf3), "%d / %d", m, n); cells[4] = buf3; cells[5] = NULL; ajax_table_row(tq, cells, csize, &o, (const char *[]){NULL, "status", "state", "name", "nsvc"}, tdmi->tdmi_identifier); } tcp_qprintf(tq, "
"); http_output_html(hc, hr); return 0; } /** * */ static int dvbsvccmp(th_transport_t *a, th_transport_t *b) { return a->tht_dvb_service_id - b->tht_dvb_service_id; } /** * Display detailes about a mux */ static int ajax_dvbmuxeditor(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { th_dvb_mux_instance_t *tdmi; tcp_queue_t *tq = &hr->hr_tq; char buf[1000]; th_transport_t *t; struct th_transport_list head; int n = 0; if(remain == NULL || (tdmi = dvb_mux_find_by_identifier(remain)) == NULL) return HTTP_STATUS_NOT_FOUND; tdmi_displayname(tdmi, buf, sizeof(buf)); LIST_INIT(&head); LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) { if(transport_is_available(t)) { LIST_INSERT_SORTED(&head, t, tht_tmp_link, dvbsvccmp); n++; } } ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, buf); ajax_transport_build_list(hc, tq, &head, n); ajax_box_end(tq, AJAX_BOX_SIDEBOX); http_output_html(hc, hr); 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); }