Work in progress on AJAX interface for tvheadend
8
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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -30,12 +30,12 @@
|
|||
|
||||
|
||||
#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();
|
||||
}
|
||||
|
|
233
ajaxui/ajaxui_config_channels.c
Normal file
|
@ -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);
|
||||
|
||||
|
||||
|
||||
}
|
662
ajaxui/ajaxui_config_dvb.c
Normal file
|
@ -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);
|
||||
|
||||
}
|
411
ajaxui/ajaxui_config_transport.c
Normal file
|
@ -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%\"> </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%\"> </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%\">"
|
||||
" </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%\"> </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\"> </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);
|
||||
}
|
BIN
ajaxui/images/mapped.png
Normal file
After Width: | Height: | Size: 448 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.1 KiB |
BIN
ajaxui/images/unmapped.png
Normal file
After Width: | Height: | Size: 495 B |
1
ajaxui/prototype/prototype.js
vendored
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 }});
|
||||
}
|
||||
}
|
||||
|
|
5
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
29
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
|
||||
|
|
4
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);
|
||||
|
|
BIN
dvb.c
14
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_ */
|
||||
|
|
|
@ -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);
|
||||
|
|
9
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 */
|
||||
|
||||
|
|
248
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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -29,8 +29,12 @@
|
|||
#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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
256
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
198
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 ?: "<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 },
|
||||
|
|
2
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:
|
||||
|
|
19
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)
|
||||
|
|
33
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)
|
||||
{
|
||||
|
|
107
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
3
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_ */
|
||||
|
|
278
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);
|
||||
}
|
||||
|
||||
|
|
15
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 */
|
||||
|
|
15
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));
|
||||
|
||||
|
|
79
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 */
|
||||
|
|
10
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;
|
||||
|
||||
|
|