Rename tvheadend_newui to tvheadend
This commit is contained in:
parent
63524f157b
commit
ce4669b418
50 changed files with 8503 additions and 7280 deletions
32
Makefile
32
Makefile
|
@ -3,7 +3,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 \
|
||||
notify.c intercom.c access.c serviceprobe.c
|
||||
notify.c intercom.c access.c serviceprobe.c dtable.c
|
||||
|
||||
SRCS += http.c
|
||||
|
||||
|
@ -34,34 +34,14 @@ SRCS += FFdecsa.c
|
|||
#
|
||||
|
||||
VPATH += webui
|
||||
SRCS += webui.c
|
||||
SRCS += webui.c extjs.c comet.c
|
||||
|
||||
#
|
||||
# Embedded AJAX user interface
|
||||
#
|
||||
JSSRCS += tvheadend.js extensions.js acleditor.js cwceditor.js \
|
||||
dvb.js
|
||||
|
||||
VPATH += ajaxui
|
||||
SRCS += ajaxui.c ajaxui_mailbox.c ajaxui_channels.c \
|
||||
ajaxui_config.c ajaxui_config_channels.c ajaxui_config_dvb.c \
|
||||
ajaxui_config_transport.c ajaxui_config_xmltv.c \
|
||||
ajaxui_config_access.c ajaxui_config_cwc.c
|
||||
|
||||
JSSRCS += tvheadend.js
|
||||
|
||||
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
|
||||
|
||||
|
||||
VPATH += ajaxui/scriptaculous
|
||||
JSSRCS += builder.js controls.js dragdrop.js effects.js scriptaculous.js \
|
||||
slider.js
|
||||
CSSSRCS += ext.css
|
||||
|
||||
PROGPATH = $(HTS_BUILD_ROOT)/tvheadend_newui
|
||||
PROG = tvheadend
|
||||
MAN = tvheadend.1
|
||||
CFLAGS += -g -Wall -Werror -O2 -mmmx
|
||||
|
|
472
access.c
472
access.c
|
@ -31,117 +31,12 @@
|
|||
|
||||
#include "tvhead.h"
|
||||
#include "access.h"
|
||||
#include "dtable.h"
|
||||
|
||||
static int atally;
|
||||
struct access_entry_queue access_entries;
|
||||
static void access_load(void);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
access_init(void)
|
||||
{
|
||||
TAILQ_INIT(&access_entries);
|
||||
access_load();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static access_entry_t *
|
||||
access_alloc(void)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
|
||||
ae = calloc(1, sizeof(access_entry_t));
|
||||
TAILQ_INSERT_TAIL(&access_entries, ae, ae_link);
|
||||
ae->ae_tally = ++atally;
|
||||
return ae;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static access_entry_t *
|
||||
access_add_network(const char *prefix)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
char buf[100];
|
||||
int prefixlen;
|
||||
char *p;
|
||||
struct in_addr ip;
|
||||
char title[100];
|
||||
|
||||
if(strlen(prefix) > 90)
|
||||
return NULL;
|
||||
|
||||
strcpy(buf, prefix);
|
||||
p = strchr(buf, '/');
|
||||
if(p) {
|
||||
*p++ = 0;
|
||||
prefixlen = atoi(p);
|
||||
if(prefixlen > 32)
|
||||
return NULL;
|
||||
} else {
|
||||
prefixlen = 32;
|
||||
}
|
||||
|
||||
ip.s_addr = inet_addr(buf);
|
||||
|
||||
ae = access_alloc();
|
||||
|
||||
ae->ae_ip.s_addr = ip.s_addr;
|
||||
ae->ae_prefixlen = prefixlen;
|
||||
ae->ae_netmask = prefixlen ? 0xffffffff << (32 - prefixlen) : 0;
|
||||
ae->ae_network = ntohl(ip.s_addr) & ae->ae_netmask;
|
||||
|
||||
ip.s_addr = htonl(ae->ae_network);
|
||||
|
||||
snprintf(title, sizeof(title), "%s/%d", inet_ntoa(ip), prefixlen);
|
||||
|
||||
ae->ae_title = strdup(title);
|
||||
return ae;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static access_entry_t *
|
||||
access_add_user(const char *username)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
|
||||
ae = access_alloc();
|
||||
ae->ae_username = strdup(username);
|
||||
ae->ae_title = strdup(username);
|
||||
return ae;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
access_entry_t *
|
||||
access_add(const char *id)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
|
||||
if(isdigit(id[0]))
|
||||
ae = access_add_network(id);
|
||||
else
|
||||
ae = access_add_user(id);
|
||||
|
||||
if(ae != NULL)
|
||||
access_save();
|
||||
|
||||
return ae;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -158,168 +53,295 @@ access_verify(const char *username, const char *password,
|
|||
|
||||
TAILQ_FOREACH(ae, &access_entries, ae_link) {
|
||||
|
||||
if(access_is_prefix(ae)) {
|
||||
if((b & ae->ae_netmask) == ae->ae_network)
|
||||
bits |= ae->ae_rights;
|
||||
if(ae->ae_username[0] != '*') {
|
||||
/* acl entry requires username to match */
|
||||
printf("Need user access\n");
|
||||
if(username == NULL)
|
||||
continue; /* Didn't get one */
|
||||
|
||||
} else {
|
||||
|
||||
if(username != NULL &&
|
||||
!strcmp(ae->ae_username, username) &&
|
||||
!strcmp(ae->ae_password, password))
|
||||
bits |= ae->ae_rights;
|
||||
if(strcmp(ae->ae_username, username) ||
|
||||
strcmp(ae->ae_password, password))
|
||||
continue; /* username/password mismatch */
|
||||
}
|
||||
}
|
||||
|
||||
if((b & ae->ae_netmask) != ae->ae_network)
|
||||
continue; /* IP based access mismatches */
|
||||
|
||||
bits |= ae->ae_rights;
|
||||
}
|
||||
return (mask & bits) == mask ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
access_entry_t *
|
||||
access_by_id(int id)
|
||||
static void
|
||||
access_update_flag(access_entry_t *ae, int flag, int bo)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
|
||||
TAILQ_FOREACH(ae, &access_entries, ae_link) {
|
||||
if(ae->ae_tally == id)
|
||||
return ae;
|
||||
}
|
||||
return NULL;
|
||||
if(bo)
|
||||
ae->ae_rights |= flag;
|
||||
else
|
||||
ae->ae_rights &= ~flag;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
access_delete(access_entry_t *ae)
|
||||
static void
|
||||
access_set_prefix(access_entry_t *ae, const char *prefix)
|
||||
{
|
||||
TAILQ_REMOVE(&access_entries, ae, ae_link);
|
||||
free(ae->ae_title);
|
||||
char buf[100];
|
||||
int prefixlen;
|
||||
char *p;
|
||||
|
||||
if(strlen(prefix) > 90)
|
||||
return;
|
||||
|
||||
strcpy(buf, prefix);
|
||||
p = strchr(buf, '/');
|
||||
if(p) {
|
||||
*p++ = 0;
|
||||
prefixlen = atoi(p);
|
||||
if(prefixlen > 32)
|
||||
return;
|
||||
} else {
|
||||
prefixlen = 32;
|
||||
}
|
||||
|
||||
ae->ae_ip.s_addr = inet_addr(buf);
|
||||
ae->ae_prefixlen = prefixlen;
|
||||
|
||||
ae->ae_netmask = prefixlen ? 0xffffffff << (32 - prefixlen) : 0;
|
||||
ae->ae_network = ntohl(ae->ae_ip.s_addr) & ae->ae_netmask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static access_entry_t *
|
||||
access_entry_find(const char *id, int create)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
char buf[20];
|
||||
|
||||
if(id != NULL) {
|
||||
TAILQ_FOREACH(ae, &access_entries, ae_link)
|
||||
if(!strcmp(ae->ae_id, id))
|
||||
return ae;
|
||||
}
|
||||
|
||||
if(create == 0)
|
||||
return NULL;
|
||||
|
||||
ae = calloc(1, sizeof(access_entry_t));
|
||||
if(id == NULL) {
|
||||
atally++;
|
||||
snprintf(buf, sizeof(buf), "%d", atally);
|
||||
id = buf;
|
||||
} else {
|
||||
atally = atoi(id);
|
||||
}
|
||||
|
||||
ae->ae_id = strdup(id);
|
||||
ae->ae_username = strdup("*");
|
||||
ae->ae_password = strdup("*");
|
||||
ae->ae_comment = strdup("New entry");
|
||||
TAILQ_INSERT_TAIL(&access_entries, ae, ae_link);
|
||||
return ae;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
access_entry_destroy(access_entry_t *ae)
|
||||
{
|
||||
free(ae->ae_id);
|
||||
free(ae->ae_username);
|
||||
free(ae->ae_password);
|
||||
TAILQ_REMOVE(&access_entries, ae, ae_link);
|
||||
free(ae);
|
||||
access_save();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
access_save_right(FILE *fp, access_entry_t *ae, const char *right, int v)
|
||||
static htsmsg_t *
|
||||
access_record_build(access_entry_t *ae)
|
||||
{
|
||||
fprintf(fp, "\t%s = %d\n", right, !!(ae->ae_rights & v));
|
||||
}
|
||||
htsmsg_t *e = htsmsg_create();
|
||||
char buf[100];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
access_load_right(struct config_head *h, access_entry_t *ae,
|
||||
const char *right, int v)
|
||||
{
|
||||
int on = atoi(config_get_str_sub(h, right, "0"));
|
||||
htsmsg_add_u32(e, "enabled", !!ae->ae_enabled);
|
||||
|
||||
htsmsg_add_str(e, "username", ae->ae_username);
|
||||
htsmsg_add_str(e, "password", ae->ae_password);
|
||||
htsmsg_add_str(e, "comment", ae->ae_comment);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/%d", inet_ntoa(ae->ae_ip), ae->ae_prefixlen);
|
||||
htsmsg_add_str(e, "prefix", buf);
|
||||
|
||||
htsmsg_add_u32(e, "streaming", ae->ae_rights & ACCESS_STREAMING ? 1 : 0);
|
||||
htsmsg_add_u32(e, "pvr" , ae->ae_rights & ACCESS_RECORDER ? 1 : 0);
|
||||
htsmsg_add_u32(e, "webui" , ae->ae_rights & ACCESS_WEB_INTERFACE ? 1 : 0);
|
||||
htsmsg_add_u32(e, "admin" , ae->ae_rights & ACCESS_ADMIN ? 1 : 0);
|
||||
|
||||
|
||||
htsmsg_add_str(e, "id", ae->ae_id);
|
||||
|
||||
if(on)
|
||||
ae->ae_rights |= v;
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
access_record_get_all(void *opaque)
|
||||
{
|
||||
htsmsg_t *r = htsmsg_create_array();
|
||||
access_entry_t *ae;
|
||||
|
||||
TAILQ_FOREACH(ae, &access_entries, ae_link)
|
||||
htsmsg_add_msg(r, NULL, access_record_build(ae));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
access_record_get(void *opaque, const char *id)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
|
||||
if((ae = access_entry_find(id, 0)) == NULL)
|
||||
return NULL;
|
||||
return access_record_build(ae);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
access_record_create(void *opaque)
|
||||
{
|
||||
return access_record_build(access_entry_find(NULL, 1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
access_record_update(void *opaque, const char *id, htsmsg_t *values,
|
||||
int maycreate)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
const char *s;
|
||||
uint32_t u32;
|
||||
|
||||
if((ae = access_entry_find(id, maycreate)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if((s = htsmsg_get_str(values, "username")) != NULL) {
|
||||
free(ae->ae_username);
|
||||
ae->ae_username = strdup(s);
|
||||
}
|
||||
|
||||
if((s = htsmsg_get_str(values, "comment")) != NULL) {
|
||||
free(ae->ae_comment);
|
||||
ae->ae_comment = strdup(s);
|
||||
}
|
||||
|
||||
if((s = htsmsg_get_str(values, "password")) != NULL) {
|
||||
free(ae->ae_password);
|
||||
ae->ae_password = strdup(s);
|
||||
}
|
||||
|
||||
if((s = htsmsg_get_str(values, "prefix")) != NULL)
|
||||
access_set_prefix(ae, s);
|
||||
|
||||
if(!htsmsg_get_u32(values, "enabled", &u32))
|
||||
ae->ae_enabled = u32;
|
||||
|
||||
if(!htsmsg_get_u32(values, "streaming", &u32))
|
||||
access_update_flag(ae, ACCESS_STREAMING, u32);
|
||||
|
||||
if(!htsmsg_get_u32(values, "pvr", &u32))
|
||||
access_update_flag(ae, ACCESS_RECORDER, u32);
|
||||
|
||||
if(!htsmsg_get_u32(values, "admin", &u32))
|
||||
access_update_flag(ae, ACCESS_ADMIN, u32);
|
||||
|
||||
if(!htsmsg_get_u32(values, "webui", &u32))
|
||||
access_update_flag(ae, ACCESS_WEB_INTERFACE, u32);
|
||||
|
||||
return access_record_build(ae);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
access_record_delete(void *opaque, const char *id)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
|
||||
if((ae = access_entry_find(id, 0)) == NULL)
|
||||
return -1;
|
||||
access_entry_destroy(ae);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static const dtable_class_t access_dtc = {
|
||||
.dtc_record_get = access_record_get,
|
||||
.dtc_record_get_all = access_record_get_all,
|
||||
.dtc_record_create = access_record_create,
|
||||
.dtc_record_update = access_record_update,
|
||||
.dtc_record_delete = access_record_delete,
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
access_save(void)
|
||||
access_init(void)
|
||||
{
|
||||
dtable_t *dt;
|
||||
htsmsg_t *r;
|
||||
access_entry_t *ae;
|
||||
char buf[400];
|
||||
FILE *fp;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/access.cfg", settings_dir);
|
||||
if((fp = settings_open_for_write(buf)) == NULL)
|
||||
return;
|
||||
TAILQ_INIT(&access_entries);
|
||||
|
||||
TAILQ_FOREACH(ae, &access_entries, ae_link) {
|
||||
if(access_is_prefix(ae)) {
|
||||
fprintf(fp, "prefix {\n");
|
||||
fprintf(fp, "\tid = %s\n", ae->ae_title);
|
||||
} else {
|
||||
fprintf(fp, "user {\n");
|
||||
fprintf(fp, "\tname = %s\n", ae->ae_username);
|
||||
if(ae->ae_password != NULL)
|
||||
fprintf(fp, "\tpassword = %s\n", ae->ae_password);
|
||||
}
|
||||
dt = dtable_create(&access_dtc, "accesscontrol", NULL);
|
||||
|
||||
access_save_right(fp, ae, "streaming", ACCESS_STREAMING);
|
||||
access_save_right(fp, ae, "rec", ACCESS_RECORDER_VIEW);
|
||||
access_save_right(fp, ae, "recedit", ACCESS_RECORDER_CHANGE);
|
||||
access_save_right(fp, ae, "admin", ACCESS_ADMIN);
|
||||
access_save_right(fp, ae, "webui", ACCESS_WEB_INTERFACE);
|
||||
access_save_right(fp, ae, "access", ACCESS_ADMIN_ACCESS);
|
||||
if(dtable_load(dt) == 0) {
|
||||
/* No records available */
|
||||
ae = access_entry_find(NULL, 1);
|
||||
|
||||
fprintf(fp, "}\n");
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
free(ae->ae_comment);
|
||||
ae->ae_comment = strdup("Default access entry");
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
access_load(void)
|
||||
{
|
||||
char buf[400];
|
||||
access_entry_t *ae;
|
||||
const char *name;
|
||||
struct config_head cl;
|
||||
config_entry_t *ce;
|
||||
ae->ae_enabled = 1;
|
||||
ae->ae_rights = 0xffffffff;
|
||||
|
||||
TAILQ_INIT(&cl);
|
||||
r = access_record_build(ae);
|
||||
dtable_record_store(dt, ae->ae_id, r);
|
||||
htsmsg_destroy(r);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/access.cfg", settings_dir);
|
||||
|
||||
if(config_read_file0(buf, &cl)) {
|
||||
ae = access_add_network("0.0.0.0/0");
|
||||
ae->ae_rights = ACCESS_FULL;
|
||||
access_save();
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "Notice: Created default access controle entry\n");
|
||||
|
||||
TAILQ_FOREACH(ce, &cl, ce_link) {
|
||||
if(ce->ce_type != CFG_SUB)
|
||||
continue;
|
||||
|
||||
if(!strcasecmp("user", ce->ce_key)) {
|
||||
|
||||
if((name = config_get_str_sub(&ce->ce_sub, "name", NULL)) == NULL)
|
||||
continue;
|
||||
|
||||
ae = access_add_user(name);
|
||||
|
||||
if((name = config_get_str_sub(&ce->ce_sub, "password", NULL)) != NULL)
|
||||
ae->ae_password = strdup(name);
|
||||
|
||||
} else if(!strcasecmp("prefix", ce->ce_key)) {
|
||||
|
||||
if((name = config_get_str_sub(&ce->ce_sub, "id", NULL)) == NULL)
|
||||
continue;
|
||||
|
||||
ae = access_add_network(name);
|
||||
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ae == NULL)
|
||||
continue;
|
||||
|
||||
access_load_right(&ce->ce_sub, ae, "streaming", ACCESS_STREAMING);
|
||||
access_load_right(&ce->ce_sub, ae, "rec", ACCESS_RECORDER_VIEW);
|
||||
access_load_right(&ce->ce_sub, ae, "recedit", ACCESS_RECORDER_CHANGE);
|
||||
access_load_right(&ce->ce_sub, ae, "admin", ACCESS_ADMIN);
|
||||
access_load_right(&ce->ce_sub, ae, "webui", ACCESS_WEB_INTERFACE);
|
||||
access_load_right(&ce->ce_sub, ae, "access", ACCESS_ADMIN_ACCESS);
|
||||
}
|
||||
}
|
||||
|
|
23
access.h
23
access.h
|
@ -24,17 +24,18 @@ TAILQ_HEAD(access_entry_queue, access_entry);
|
|||
|
||||
extern struct access_entry_queue access_entries;
|
||||
|
||||
#define access_is_prefix(ae) ((ae)->ae_username == NULL)
|
||||
|
||||
typedef struct access_entry {
|
||||
int ae_tally;
|
||||
char *ae_title;
|
||||
char *ae_id;
|
||||
|
||||
TAILQ_ENTRY(access_entry) ae_link;
|
||||
char *ae_username;
|
||||
char *ae_password;
|
||||
char *ae_comment;
|
||||
struct in_addr ae_ip;
|
||||
int ae_prefixlen;
|
||||
int ae_enabled;
|
||||
|
||||
|
||||
uint32_t ae_rights;
|
||||
|
||||
uint32_t ae_network; /* derived from ae_ip */
|
||||
|
@ -43,11 +44,9 @@ typedef struct access_entry {
|
|||
|
||||
|
||||
#define ACCESS_STREAMING 0x1
|
||||
#define ACCESS_RECORDER_VIEW 0x2
|
||||
#define ACCESS_RECORDER_CHANGE 0x4
|
||||
#define ACCESS_WEB_INTERFACE 0x2
|
||||
#define ACCESS_RECORDER 0x4
|
||||
#define ACCESS_ADMIN 0x8
|
||||
#define ACCESS_WEB_INTERFACE 0x10
|
||||
#define ACCESS_ADMIN_ACCESS 0x20
|
||||
|
||||
#define ACCESS_FULL 0x3f
|
||||
|
||||
|
@ -62,12 +61,12 @@ int access_verify(const char *username, const char *password,
|
|||
|
||||
void access_init(void);
|
||||
|
||||
access_entry_t *access_add(const char *id);
|
||||
//access_entry_t *access_add(const char *id);
|
||||
|
||||
access_entry_t *access_by_id(int id);
|
||||
//access_entry_t *access_by_id(int id);
|
||||
|
||||
void access_delete(access_entry_t *ae);
|
||||
//void access_delete(access_entry_t *ae);
|
||||
|
||||
void access_save(void);
|
||||
//void access_save(void);
|
||||
|
||||
#endif /* ACCESS_H_ */
|
||||
|
|
305
cwc.c
305
cwc.c
|
@ -39,6 +39,8 @@
|
|||
#include "cwc.h"
|
||||
#include "notify.h"
|
||||
|
||||
#include "dtable.h"
|
||||
|
||||
static int cwc_tally;
|
||||
|
||||
#define CWC_KEEPALIVE_INTERVAL 600
|
||||
|
@ -119,7 +121,7 @@ cwc_set_state(cwc_t *cwc, int newstate, const char *errtxt)
|
|||
if(newstate == CWC_STATE_IDLE)
|
||||
cwc->cwc_errtxt = errtxt;
|
||||
|
||||
notify_cwc_status_change(cwc);
|
||||
//notify_cwc_status_change(cwc);
|
||||
}
|
||||
|
||||
|
||||
|
@ -263,8 +265,8 @@ des_make_login_key(cwc_t *cwc, uint8_t *k)
|
|||
static void
|
||||
des_make_session_key(cwc_t *cwc)
|
||||
{
|
||||
uint8_t des14[14], *k2 = (uint8_t *)cwc->cwc_password;
|
||||
int i, l = strlen(cwc->cwc_password);
|
||||
uint8_t des14[14], *k2 = (uint8_t *)cwc->cwc_password_salted;
|
||||
int i, l = strlen(cwc->cwc_password_salted);
|
||||
|
||||
memcpy(des14, cwc->cwc_confedkey, 14);
|
||||
|
||||
|
@ -385,7 +387,7 @@ cwc_dispatch_card_data_reply(cwc_t *cwc, uint8_t msgtype,
|
|||
cwc->cwc_caid = (msg[4] << 8) | msg[5];
|
||||
n = psi_caid2name(cwc->cwc_caid) ?: "Unknown";
|
||||
|
||||
syslog(LOG_INFO, "cwc: %s: Connected as user 0x%02x "
|
||||
tvhlog(LOG_INFO, "cwc", "%s: Connected as user 0x%02x "
|
||||
"to a %s-card [0x%04x : %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x] "
|
||||
"with %d providers",
|
||||
cwc->cwc_tcp_session.tcp_hostname,
|
||||
|
@ -412,7 +414,7 @@ cwc_send_login(cwc_t *cwc)
|
|||
{
|
||||
uint8_t buf[CWS_NETMSGSIZE];
|
||||
int ul = strlen(cwc->cwc_username) + 1;
|
||||
int pl = strlen(cwc->cwc_password) + 1;
|
||||
int pl = strlen(cwc->cwc_password_salted) + 1;
|
||||
|
||||
cwc_set_state(cwc, CWC_STATE_WAIT_LOGIN_ACK, NULL);
|
||||
|
||||
|
@ -420,7 +422,7 @@ cwc_send_login(cwc_t *cwc)
|
|||
buf[1] = 0;
|
||||
buf[2] = ul + pl;
|
||||
memcpy(buf + 3, cwc->cwc_username, ul);
|
||||
memcpy(buf + 3 + ul, cwc->cwc_password, pl);
|
||||
memcpy(buf + 3 + ul, cwc->cwc_password_salted, pl);
|
||||
|
||||
cwc_send_msg(cwc, buf, ul + pl + 3, 0);
|
||||
}
|
||||
|
@ -434,14 +436,21 @@ cwc_dispatch_login_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
|
|||
{
|
||||
switch(msgtype) {
|
||||
case MSG_CLIENT_2_SERVER_LOGIN_ACK:
|
||||
tvhlog(LOG_INFO, "cwc", "%s: Login ok",
|
||||
cwc->cwc_tcp_session.tcp_hostname);
|
||||
des_make_session_key(cwc);
|
||||
cwc_send_data_req(cwc);
|
||||
return 0;
|
||||
|
||||
case MSG_CLIENT_2_SERVER_LOGIN_NAK:
|
||||
tvhlog(LOG_INFO, "cwc", "%s: Authentication denied",
|
||||
cwc->cwc_tcp_session.tcp_hostname);
|
||||
return EACCES;
|
||||
|
||||
default:
|
||||
tvhlog(LOG_INFO, "cwc", "%s: Invalid response (msgcode = %d) during login",
|
||||
cwc->cwc_tcp_session.tcp_hostname, msgtype);
|
||||
|
||||
return EBADMSG;
|
||||
}
|
||||
}
|
||||
|
@ -479,7 +488,7 @@ cwc_dispatch_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
|
|||
if(len < 19) {
|
||||
|
||||
if(ct->ct_keystate != CT_FORBIDDEN) {
|
||||
syslog(LOG_ERR,
|
||||
tvhlog(LOG_ERR, "cwc",
|
||||
"Can not descramble \"%s\" for service \"%s\", access denied",
|
||||
t->tht_identifier, t->tht_svcname);
|
||||
ct->ct_keystate = CT_FORBIDDEN;
|
||||
|
@ -489,7 +498,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,
|
||||
tvhlog(LOG_INFO, "cwc",
|
||||
"Obtained key for \"%s\" for service \"%s\"",
|
||||
t->tht_identifier, t->tht_svcname);
|
||||
|
||||
|
@ -774,6 +783,7 @@ cwc_transport_start(th_transport_t *t)
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -914,25 +924,14 @@ cwc_add(const char *hostname, const char *porttxt, const char *username,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
cwc_t *
|
||||
cwc_find(int id)
|
||||
{
|
||||
cwc_t *cwc;
|
||||
TAILQ_FOREACH(cwc, &cwcs, cwc_link)
|
||||
if(cwc->cwc_id == id)
|
||||
break;
|
||||
return cwc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
cwc_delete(cwc_t *cwc)
|
||||
static void
|
||||
cwc_destroy(cwc_t *cwc)
|
||||
{
|
||||
cwc_transport_t *ct;
|
||||
|
||||
|
@ -941,22 +940,251 @@ cwc_delete(cwc_t *cwc)
|
|||
|
||||
TAILQ_REMOVE(&cwcs, cwc, cwc_link);
|
||||
free((void *)cwc->cwc_password);
|
||||
free((void *)cwc->cwc_password_salted);
|
||||
free((void *)cwc->cwc_username);
|
||||
|
||||
tcp_destroy_client(&cwc->cwc_tcp_session);
|
||||
cwc_save();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static cwc_t *
|
||||
cwc_entry_find(const char *id, int create)
|
||||
{
|
||||
char buf[20];
|
||||
cwc_t *cwc;
|
||||
|
||||
if(id != NULL) {
|
||||
TAILQ_FOREACH(cwc, &cwcs, cwc_link)
|
||||
if(!strcmp(cwc->cwc_id, id))
|
||||
return cwc;
|
||||
}
|
||||
if(create == 0)
|
||||
return NULL;
|
||||
|
||||
if(id == NULL) {
|
||||
cwc_tally++;
|
||||
snprintf(buf, sizeof(buf), "%d", cwc_tally);
|
||||
id = buf;
|
||||
} else {
|
||||
cwc_tally = atoi(id);
|
||||
}
|
||||
|
||||
cwc = tcp_create_client(NULL, 11000, sizeof(cwc_t),
|
||||
"cwc", cwc_tcp_callback, 0);
|
||||
|
||||
cwc->cwc_id = strdup(id);
|
||||
cwc->cwc_username = NULL;
|
||||
cwc->cwc_password = NULL;
|
||||
|
||||
TAILQ_INSERT_TAIL(&cwcs, cwc, cwc_link);
|
||||
return cwc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
cwc_record_build(cwc_t *cwc)
|
||||
{
|
||||
htsmsg_t *e = htsmsg_create();
|
||||
char buf[100];
|
||||
|
||||
htsmsg_add_str(e, "id", cwc->cwc_id);
|
||||
htsmsg_add_u32(e, "enabled", !!cwc->cwc_enabled);
|
||||
|
||||
htsmsg_add_str(e, "hostname", cwc->cwc_tcp_session.tcp_hostname ?: "");
|
||||
htsmsg_add_u32(e, "port", cwc->cwc_tcp_session.tcp_port);
|
||||
|
||||
htsmsg_add_str(e, "username", cwc->cwc_username ?: "");
|
||||
htsmsg_add_str(e, "password", cwc->cwc_password ?: "");
|
||||
snprintf(buf, sizeof(buf),
|
||||
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
|
||||
"%02x:%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
cwc->cwc_confedkey[0x0],
|
||||
cwc->cwc_confedkey[0x1],
|
||||
cwc->cwc_confedkey[0x2],
|
||||
cwc->cwc_confedkey[0x3],
|
||||
cwc->cwc_confedkey[0x4],
|
||||
cwc->cwc_confedkey[0x5],
|
||||
cwc->cwc_confedkey[0x6],
|
||||
cwc->cwc_confedkey[0x7],
|
||||
cwc->cwc_confedkey[0x8],
|
||||
cwc->cwc_confedkey[0x9],
|
||||
cwc->cwc_confedkey[0xa],
|
||||
cwc->cwc_confedkey[0xb],
|
||||
cwc->cwc_confedkey[0xc],
|
||||
cwc->cwc_confedkey[0xd]);
|
||||
|
||||
htsmsg_add_str(e, "deskey", buf);
|
||||
|
||||
htsmsg_add_str(e, "comment", cwc->cwc_comment ?: "");
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
nibble(char c)
|
||||
{
|
||||
switch(c) {
|
||||
case '0' ... '9':
|
||||
return c - '0';
|
||||
case 'a' ... 'f':
|
||||
return c - 'a' + 10;
|
||||
case 'A' ... 'F':
|
||||
return c - 'A' + 10;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
cwc_entry_update(void *opaque, const char *id, htsmsg_t *values, int maycreate)
|
||||
{
|
||||
cwc_t *cwc;
|
||||
const char *s;
|
||||
uint32_t u32;
|
||||
uint8_t key[14];
|
||||
int u, l, i;
|
||||
|
||||
if((cwc = cwc_entry_find(id, maycreate)) == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Disable while changing */
|
||||
tcp_enable_disable(&cwc->cwc_tcp_session, 0);
|
||||
|
||||
if((s = htsmsg_get_str(values, "username")) != NULL) {
|
||||
free(cwc->cwc_username);
|
||||
cwc->cwc_username = strdup(s);
|
||||
}
|
||||
|
||||
if((s = htsmsg_get_str(values, "password")) != NULL) {
|
||||
free(cwc->cwc_password);
|
||||
free(cwc->cwc_password_salted);
|
||||
cwc->cwc_password = strdup(s);
|
||||
cwc->cwc_password_salted = strdup(cwc_krypt(s, "$1$abcdefgh$"));
|
||||
}
|
||||
|
||||
if((s = htsmsg_get_str(values, "comment")) != NULL) {
|
||||
free(cwc->cwc_comment);
|
||||
cwc->cwc_comment = strdup(s);
|
||||
}
|
||||
|
||||
if((s = htsmsg_get_str(values, "hostname")) != NULL) {
|
||||
tcp_set_hostname(&cwc->cwc_tcp_session, s);
|
||||
}
|
||||
|
||||
if(!htsmsg_get_u32(values, "enabled", &u32))
|
||||
cwc->cwc_enabled = u32;
|
||||
|
||||
if(!htsmsg_get_u32(values, "port", &u32))
|
||||
cwc->cwc_tcp_session.tcp_port = u32;
|
||||
|
||||
if((s = htsmsg_get_str(values, "deskey")) != NULL) {
|
||||
for(i = 0; i < 14; i++) {
|
||||
while(*s != 0 && !isxdigit(*s)) s++;
|
||||
if(*s == 0)
|
||||
break;
|
||||
u = nibble(*s++);
|
||||
while(*s != 0 && !isxdigit(*s)) s++;
|
||||
if(*s == 0)
|
||||
break;
|
||||
l = nibble(*s++);
|
||||
key[i] = (u << 4) | l;
|
||||
}
|
||||
memcpy(cwc->cwc_confedkey, key, 14);
|
||||
}
|
||||
|
||||
if(cwc->cwc_enabled)
|
||||
tcp_enable_disable(&cwc->cwc_tcp_session, 1);
|
||||
|
||||
return cwc_record_build(cwc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
cwc_entry_delete(void *opaque, const char *id)
|
||||
{
|
||||
cwc_t *cwc;
|
||||
|
||||
if((cwc = cwc_entry_find(id, 0)) == NULL)
|
||||
return -1;
|
||||
cwc_destroy(cwc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
cwc_entry_get_all(void *opaque)
|
||||
{
|
||||
htsmsg_t *r = htsmsg_create_array();
|
||||
cwc_t *cwc;
|
||||
|
||||
TAILQ_FOREACH(cwc, &cwcs, cwc_link)
|
||||
htsmsg_add_msg(r, NULL, cwc_record_build(cwc));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
cwc_set_enable(cwc_t *cwc, int enabled)
|
||||
static htsmsg_t *
|
||||
cwc_entry_get(void *opaque, const char *id)
|
||||
{
|
||||
tcp_enable_disable(&cwc->cwc_tcp_session, enabled);
|
||||
cwc_save();
|
||||
cwc_t *cwc;
|
||||
|
||||
|
||||
if((cwc = cwc_entry_find(id, 0)) == NULL)
|
||||
return NULL;
|
||||
return cwc_record_build(cwc);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
cwc_entry_create(void *opaque)
|
||||
{
|
||||
return cwc_record_build(cwc_entry_find(NULL, 1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static const dtable_class_t cwc_dtc = {
|
||||
.dtc_record_get = cwc_entry_get,
|
||||
.dtc_record_get_all = cwc_entry_get_all,
|
||||
.dtc_record_create = cwc_entry_create,
|
||||
.dtc_record_update = cwc_entry_update,
|
||||
.dtc_record_delete = cwc_entry_delete,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -964,29 +1192,12 @@ cwc_set_enable(cwc_t *cwc, int enabled)
|
|||
void
|
||||
cwc_init(void)
|
||||
{
|
||||
struct config_head cl;
|
||||
config_entry_t *ce;
|
||||
char buf[400];
|
||||
dtable_t *dt;
|
||||
|
||||
TAILQ_INIT(&cwcs);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/cwc.cfg", settings_dir);
|
||||
|
||||
TAILQ_INIT(&cl);
|
||||
config_read_file0(buf, &cl);
|
||||
|
||||
TAILQ_FOREACH(ce, &cl, ce_link) {
|
||||
if(ce->ce_type != CFG_SUB || strcasecmp("cwc", ce->ce_key))
|
||||
continue;
|
||||
|
||||
cwc_add(config_get_str_sub(&ce->ce_sub, "hostname", NULL),
|
||||
config_get_str_sub(&ce->ce_sub, "port", NULL),
|
||||
config_get_str_sub(&ce->ce_sub, "username", NULL),
|
||||
config_get_str_sub(&ce->ce_sub, "password", NULL),
|
||||
config_get_str_sub(&ce->ce_sub, "deskey", NULL),
|
||||
config_get_str_sub(&ce->ce_sub, "enabled", NULL),
|
||||
0, 0);
|
||||
}
|
||||
dt = dtable_create(&cwc_dtc, "cwc", NULL);
|
||||
dtable_load(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
10
cwc.h
10
cwc.h
|
@ -54,17 +54,21 @@ typedef struct cwc {
|
|||
/* From configuration */
|
||||
|
||||
uint8_t cwc_confedkey[14];
|
||||
const char *cwc_username;
|
||||
const char *cwc_password; /* salted version */
|
||||
char *cwc_username;
|
||||
char *cwc_password;
|
||||
char *cwc_password_salted; /* salted version */
|
||||
char *cwc_comment;
|
||||
|
||||
dtimer_t cwc_idle_timer;
|
||||
|
||||
dtimer_t cwc_send_ka_timer;
|
||||
|
||||
int cwc_id;
|
||||
char *cwc_id;
|
||||
|
||||
const char *cwc_errtxt;
|
||||
|
||||
int cwc_enabled;
|
||||
|
||||
} cwc_t;
|
||||
|
||||
|
||||
|
|
192
dtable.c
Normal file
192
dtable.c
Normal file
|
@ -0,0 +1,192 @@
|
|||
/**
|
||||
* Dtable (dyanmic, data, etc) table
|
||||
* 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 <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libhts/htssettings.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dtable.h"
|
||||
|
||||
static LIST_HEAD(, dtable) dtables;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dtable_t *
|
||||
dtable_create(const dtable_class_t *dtc, const char *name, void *opaque)
|
||||
{
|
||||
dtable_t *dt = calloc(1, sizeof(dtable_t));
|
||||
|
||||
dt->dt_opaque = opaque;
|
||||
dt->dt_tablename = strdup(name);
|
||||
dt->dt_dtc = dtc;
|
||||
|
||||
LIST_INSERT_HEAD(&dtables, dt, dt_link);
|
||||
return dt;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int
|
||||
dtable_load(dtable_t *dt)
|
||||
{
|
||||
htsmsg_t *l, *c, *m;
|
||||
htsmsg_field_t *f;
|
||||
const char *id;
|
||||
|
||||
int records = 0;
|
||||
|
||||
if((l = hts_settings_load("%s", dt->dt_tablename)) != NULL) {
|
||||
HTSMSG_FOREACH(f, l) {
|
||||
if((c = htsmsg_get_msg_by_field(f)) == NULL)
|
||||
continue;
|
||||
|
||||
if((id = htsmsg_get_str(c, "id")) == NULL)
|
||||
continue;
|
||||
|
||||
m = dt->dt_dtc->dtc_record_update(dt->dt_opaque, id, c, 1);
|
||||
if(m != NULL) {
|
||||
records++;
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dtable_t *
|
||||
dtable_find(const char *name)
|
||||
{
|
||||
dtable_t *dt;
|
||||
LIST_FOREACH(dt, &dtables, dt_link)
|
||||
if(!strcmp(dt->dt_tablename, name))
|
||||
break;
|
||||
return dt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int
|
||||
dtable_record_update_by_array(dtable_t *dt, htsmsg_t *msg)
|
||||
{
|
||||
htsmsg_t *c, *update;
|
||||
htsmsg_field_t *f;
|
||||
const char *id;
|
||||
|
||||
TAILQ_FOREACH(f, &msg->hm_fields, hmf_link) {
|
||||
if((c = htsmsg_get_msg_by_field(f)) == NULL)
|
||||
continue;
|
||||
if((id = htsmsg_get_str(c, "id")) == NULL)
|
||||
continue;
|
||||
|
||||
if((update = dt->dt_dtc->dtc_record_update(dt->dt_opaque, id, c, 0))
|
||||
!= NULL) {
|
||||
/* Data changed */
|
||||
hts_settings_save(update, "%s/%s", dt->dt_tablename, id);
|
||||
htsmsg_destroy(update);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dtable_record_delete(dtable_t *dt, const char *id)
|
||||
{
|
||||
dt->dt_dtc->dtc_record_delete(dt->dt_opaque, id);
|
||||
hts_settings_remove("%s/%s", dt->dt_tablename, id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int
|
||||
dtable_record_delete_by_array(dtable_t *dt, htsmsg_t *msg)
|
||||
{
|
||||
htsmsg_field_t *f;
|
||||
const char *id;
|
||||
|
||||
TAILQ_FOREACH(f, &msg->hm_fields, hmf_link) {
|
||||
if((id = htsmsg_field_get_string(f)) != NULL)
|
||||
dtable_record_delete(dt, id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
htsmsg_t *
|
||||
dtable_record_create(dtable_t *dt)
|
||||
{
|
||||
htsmsg_t *r;
|
||||
const char *id;
|
||||
|
||||
if((r = dt->dt_dtc->dtc_record_create(dt->dt_opaque)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if((id = htsmsg_get_str(r, "id")) == NULL) {
|
||||
htsmsg_destroy(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hts_settings_save(r, "%s/%s", dt->dt_tablename, id);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
htsmsg_t *
|
||||
dtable_record_get_all(dtable_t *dt)
|
||||
{
|
||||
return dt->dt_dtc->dtc_record_get_all(dt->dt_opaque);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dtable_record_store(dtable_t *dt, const char *id, htsmsg_t *r)
|
||||
{
|
||||
hts_settings_save(r, "%s/%s", dt->dt_tablename, id);
|
||||
}
|
72
dtable.h
Normal file
72
dtable.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Dtable (dyanmic, data, etc) table
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef DTABLE_H__
|
||||
#define DTABLE_H__
|
||||
|
||||
#include <libhts/htsmsg.h>
|
||||
#include <libhts/htsbuf.h>
|
||||
|
||||
|
||||
typedef struct dtable_class {
|
||||
const char *dtc_name;
|
||||
|
||||
htsmsg_t *(*dtc_record_get_all)(void *opaque);
|
||||
|
||||
htsmsg_t *(*dtc_record_get)(void *opaque, const char *id);
|
||||
|
||||
htsmsg_t *(*dtc_record_create)(void *opaque);
|
||||
|
||||
htsmsg_t *(*dtc_record_update)(void *opaque, const char *id,
|
||||
htsmsg_t *values, int maycreate);
|
||||
|
||||
int (*dtc_record_delete)(void *opaque, const char *id);
|
||||
|
||||
} dtable_class_t;
|
||||
|
||||
|
||||
typedef struct dtable {
|
||||
LIST_ENTRY(dtable) dt_link;
|
||||
|
||||
void *dt_opaque;
|
||||
char *dt_tablename;
|
||||
|
||||
const dtable_class_t *dt_dtc;
|
||||
|
||||
} dtable_t;
|
||||
|
||||
dtable_t *dtable_create(const dtable_class_t *dtc, const char *name,
|
||||
void *opaque);
|
||||
|
||||
int dtable_load(dtable_t *dt);
|
||||
|
||||
dtable_t *dtable_find(const char *name);
|
||||
|
||||
int dtable_record_update_by_array(dtable_t *dt, htsmsg_t *msg);
|
||||
|
||||
void dtable_record_delete(dtable_t *dt, const char *id);
|
||||
|
||||
int dtable_record_delete_by_array(dtable_t *dt, htsmsg_t *msg);
|
||||
|
||||
htsmsg_t *dtable_record_create(dtable_t *dt);
|
||||
|
||||
htsmsg_t *dtable_record_get_all(dtable_t *dt);
|
||||
|
||||
void dtable_record_store(dtable_t *dt, const char *id, htsmsg_t *r);
|
||||
|
||||
#endif /* DTABLE_H__ */
|
294
dvb.c
294
dvb.c
|
@ -34,7 +34,7 @@
|
|||
#include <linux/dvb/frontend.h>
|
||||
#include <linux/dvb/dmx.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
#include <libhts/htssettings.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dispatch.h"
|
||||
|
@ -55,12 +55,11 @@ struct th_dvb_mux_instance_tree dvb_muxes;
|
|||
static void dvb_mux_scanner(void *aux, int64_t now);
|
||||
static void dvb_fec_monitor(void *aux, int64_t now);
|
||||
|
||||
static int dvb_tda_load(th_dvb_adapter_t *tda);
|
||||
static void dvb_tdmi_load(th_dvb_mux_instance_t *tdmi);
|
||||
static void dvb_transport_config_change(th_transport_t *t);
|
||||
static void dvb_transport_save(th_transport_t *t);
|
||||
static const char *dvb_source_name(th_transport_t *t);
|
||||
static int dvb_transport_quality(th_transport_t *t);
|
||||
|
||||
|
||||
static th_dvb_adapter_t *
|
||||
tda_alloc(void)
|
||||
{
|
||||
|
@ -86,7 +85,8 @@ dvb_add_adapter(const char *path)
|
|||
fe = open(fname, O_RDWR | O_NONBLOCK);
|
||||
if(fe == -1) {
|
||||
if(errno != ENOENT)
|
||||
syslog(LOG_ALERT, "Unable to open %s -- %s\n", fname, strerror(errno));
|
||||
tvhlog(LOG_ALERT, "dvb",
|
||||
"Unable to open %s -- %s\n", fname, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ dvb_add_adapter(const char *path)
|
|||
tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info));
|
||||
|
||||
if(ioctl(tda->tda_fe_fd, FE_GET_INFO, tda->tda_fe_info)) {
|
||||
syslog(LOG_ALERT, "%s: Unable to query adapter\n", fname);
|
||||
tvhlog(LOG_ALERT, "dvb", "%s: Unable to query adapter\n", fname);
|
||||
close(fe);
|
||||
free(tda);
|
||||
return;
|
||||
|
@ -134,11 +134,10 @@ dvb_add_adapter(const char *path)
|
|||
|
||||
tda->tda_displayname = strdup(tda->tda_fe_info->name);
|
||||
|
||||
syslog(LOG_INFO, "Found adapter %s (%s)", path, tda->tda_fe_info->name);
|
||||
tvhlog(LOG_INFO, "dvb",
|
||||
"Found adapter %s (%s)", path, tda->tda_fe_info->name);
|
||||
|
||||
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
|
||||
dvb_tda_load(tda);
|
||||
|
||||
dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1);
|
||||
dvb_fe_start(tda);
|
||||
}
|
||||
|
@ -150,9 +149,10 @@ void
|
|||
dvb_init(void)
|
||||
{
|
||||
char path[200];
|
||||
struct dirent *d;
|
||||
DIR *dir;
|
||||
int i;
|
||||
htsmsg_t *l, *c;
|
||||
htsmsg_field_t *f;
|
||||
const char *name, *s;
|
||||
int i, type;
|
||||
th_dvb_adapter_t *tda;
|
||||
|
||||
TAILQ_INIT(&dvb_adapters);
|
||||
|
@ -162,37 +162,38 @@ dvb_init(void)
|
|||
dvb_add_adapter(path);
|
||||
}
|
||||
|
||||
/* Load any stale adapters */
|
||||
l = hts_settings_load("dvbadapters");
|
||||
if(l != NULL) {
|
||||
HTSMSG_FOREACH(f, l) {
|
||||
if((c = htsmsg_get_msg_by_field(f)) == NULL)
|
||||
continue;
|
||||
|
||||
if(dvb_adapter_find_by_identifier(f->hmf_name) != NULL) {
|
||||
/* Already loaded */
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/dvbadapters", settings_dir);
|
||||
if((name = htsmsg_get_str(c, "displayname")) == NULL)
|
||||
continue;
|
||||
|
||||
if((dir = opendir(path)) == NULL)
|
||||
return;
|
||||
if((s = htsmsg_get_str(c, "type")) == NULL ||
|
||||
(type = dvb_str_to_adaptertype(s)) < 0)
|
||||
continue;
|
||||
|
||||
while((d = readdir(dir)) != NULL) {
|
||||
if(d->d_name[0] != '_')
|
||||
continue;
|
||||
tda = tda_alloc();
|
||||
tda->tda_identifier = strdup(f->hmf_name);
|
||||
tda->tda_displayname = strdup(name);
|
||||
tda->tda_type = type;
|
||||
|
||||
if(dvb_adapter_find_by_identifier(d->d_name) != NULL) {
|
||||
/* Already loaded */
|
||||
printf("%s is already loaded\n", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
tda = tda_alloc();
|
||||
|
||||
tda = calloc(1, sizeof(th_dvb_adapter_t));
|
||||
tda->tda_identifier = strdup(d->d_name);
|
||||
printf("Loading stale adapter %s\n", tda->tda_identifier);
|
||||
|
||||
if(dvb_tda_load(tda) == 0) {
|
||||
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
|
||||
} else {
|
||||
printf("Error while loading adapter %s -- settings file is corrupt\n",
|
||||
tda->tda_identifier);
|
||||
|
||||
}
|
||||
htsmsg_destroy(l);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link)
|
||||
dvb_tdmi_load(tda);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -232,7 +233,7 @@ dvb_find_transport(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
|
|||
|
||||
t->tht_start_feed = dvb_start_feed;
|
||||
t->tht_stop_feed = dvb_stop_feed;
|
||||
t->tht_config_change = dvb_transport_config_change;
|
||||
t->tht_config_change = dvb_transport_save;
|
||||
t->tht_sourcename = dvb_source_name;
|
||||
t->tht_dvb_mux_instance = tdmi;
|
||||
t->tht_quality_index = dvb_transport_quality;
|
||||
|
@ -242,6 +243,47 @@ dvb_find_transport(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvb_notify_mux_quality(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
|
||||
|
||||
htsmsg_add_u32(m, "quality", 100 + tdmi->tdmi_quality * 2);
|
||||
notify_by_msg("dvbmux", m);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvb_notify_mux_status(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
|
||||
|
||||
htsmsg_add_str(m, "status", tdmi->tdmi_last_status);
|
||||
notify_by_msg("dvbmux", m);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvb_adapter_notify_reload(th_dvb_adapter_t *tda)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
htsmsg_add_str(m, "id", tda->tda_identifier);
|
||||
|
||||
htsmsg_add_u32(m, "reload", 1);
|
||||
notify_by_msg("dvbadapter", m);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -271,7 +313,7 @@ dvb_fec_monitor(void *aux, int64_t now)
|
|||
|
||||
if(v == TDMI_FEC_ERR_HISTOGRAM_SIZE) {
|
||||
if(LIST_FIRST(&tda->tda_transports) != NULL) {
|
||||
syslog(LOG_ERR,
|
||||
tvhlog(LOG_ERR, "dvb",
|
||||
"\"%s\": Constant rate of FEC errors (average at %d / s), "
|
||||
"last %d seconds, flushing subscribers\n",
|
||||
tdmi->tdmi_identifier, vv,
|
||||
|
@ -286,13 +328,13 @@ dvb_fec_monitor(void *aux, int64_t now)
|
|||
if(dvb_mux_status(tdmi, 1) != NULL) {
|
||||
if(tdmi->tdmi_quality > -50) {
|
||||
tdmi->tdmi_quality--;
|
||||
notify_tdmi_qual_change(tdmi);
|
||||
dvb_notify_mux_quality(tdmi);
|
||||
}
|
||||
} else {
|
||||
|
||||
if(tdmi->tdmi_quality < 0) {
|
||||
tdmi->tdmi_quality++;
|
||||
notify_tdmi_qual_change(tdmi);
|
||||
dvb_notify_mux_quality(tdmi);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,7 +342,7 @@ dvb_fec_monitor(void *aux, int64_t now)
|
|||
|
||||
if(s != tdmi->tdmi_last_status) {
|
||||
tdmi->tdmi_last_status = s;
|
||||
notify_tdmi_status_change(tdmi);
|
||||
dvb_notify_mux_status(tdmi);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,7 +416,7 @@ tdmi_global_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b)
|
|||
th_dvb_mux_instance_t *
|
||||
dvb_mux_create(th_dvb_adapter_t *tda, struct dvb_frontend_parameters *fe_param,
|
||||
int polarisation, int switchport,
|
||||
uint16_t tsid, const char *network, int flags)
|
||||
uint16_t tsid, const char *network, const char *source)
|
||||
{
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
static th_dvb_mux_instance_t *skel;
|
||||
|
@ -430,11 +472,12 @@ dvb_mux_create(th_dvb_adapter_t *tda, struct dvb_frontend_parameters *fe_param,
|
|||
|
||||
RB_INSERT_SORTED(&dvb_muxes, tdmi, tdmi_global_link, tdmi_global_cmp);
|
||||
|
||||
if(flags & DVB_MUX_SAVE) {
|
||||
dvb_tda_save(tda);
|
||||
notify_tda_change(tda);
|
||||
} else if(flags & DVB_MUX_LOAD) {
|
||||
dvb_tdmi_load(tdmi);
|
||||
if(source != NULL) {
|
||||
dvb_mux_nicename(buf, sizeof(buf), tdmi);
|
||||
tvhlog(LOG_NOTICE, "dvb", "New mux \"%s\" created by %s", buf, source);
|
||||
|
||||
dvb_tdmi_save(tdmi);
|
||||
dvb_adapter_notify_reload(tda);
|
||||
}
|
||||
|
||||
return tdmi;
|
||||
|
@ -483,44 +526,18 @@ dvb_mux_destroy(th_dvb_mux_instance_t *tdmi)
|
|||
if(tdmi->tdmi_quickscan == TDMI_QUICKSCAN_WAITING)
|
||||
RB_REMOVE(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link);
|
||||
|
||||
hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier);
|
||||
|
||||
pthread_mutex_lock(&tda->tda_lock);
|
||||
dvb_fe_flush(tdmi);
|
||||
dvb_mux_unref(tdmi);
|
||||
pthread_mutex_unlock(&tda->tda_lock);
|
||||
|
||||
dvb_tda_save(tda);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Save config for the given adapter
|
||||
*/
|
||||
void
|
||||
dvb_tda_save(th_dvb_adapter_t *tda)
|
||||
{
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
FILE *fp;
|
||||
char buf[400];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/dvbadapters/%s",
|
||||
settings_dir, tda->tda_identifier);
|
||||
if((fp = settings_open_for_write(buf)) == NULL)
|
||||
return;
|
||||
|
||||
fprintf(fp, "type = %s\n", dvb_adaptertype_to_str(tda->tda_type));
|
||||
fprintf(fp, "displayname = %s\n", tda->tda_displayname);
|
||||
|
||||
RB_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
|
||||
|
||||
fprintf(fp, "mux {\n");
|
||||
dvb_mux_store(fp, tdmi);
|
||||
fprintf(fp, "}\n");
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
#if 0
|
||||
|
||||
/**
|
||||
* Load config for the given adapter
|
||||
|
@ -588,7 +605,8 @@ dvb_tda_load(th_dvb_adapter_t *tda)
|
|||
0);
|
||||
|
||||
if(v != NULL)
|
||||
syslog(LOG_ALERT, "Unable to init saved mux on %s -- %s\n",
|
||||
tvhlog(LOG_ALERT, "dvb",
|
||||
"Unable to init saved mux on %s -- %s\n",
|
||||
tda->tda_identifier, v);
|
||||
}
|
||||
config_free0(&cl);
|
||||
|
@ -597,117 +615,36 @@ dvb_tda_load(th_dvb_adapter_t *tda)
|
|||
config_free0(&cl);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Save config for the given mux
|
||||
*/
|
||||
void
|
||||
dvb_tdmi_save(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
th_transport_t *t;
|
||||
FILE *fp;
|
||||
char buf[400];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/dvbmuxes/%s",
|
||||
settings_dir, tdmi->tdmi_identifier);
|
||||
if((fp = settings_open_for_write(buf)) == NULL)
|
||||
return;
|
||||
|
||||
LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) {
|
||||
fprintf(fp, "service {\n");
|
||||
fprintf(fp, "\tservice_id = %d\n", t->tht_dvb_service_id);
|
||||
fprintf(fp, "\tpmt = %d\n", t->tht_pmt);
|
||||
fprintf(fp, "\tstype = %d\n", t->tht_servicetype);
|
||||
fprintf(fp, "\tscrambled = %d\n", t->tht_scrambled);
|
||||
|
||||
if(t->tht_provider != NULL)
|
||||
fprintf(fp, "\tprovider = %s\n", t->tht_provider);
|
||||
|
||||
if(t->tht_svcname)
|
||||
fprintf(fp, "\tservicename = %s\n", t->tht_svcname);
|
||||
|
||||
if(t->tht_chname)
|
||||
fprintf(fp, "\tchannelname = %s\n", t->tht_chname);
|
||||
|
||||
fprintf(fp, "\tmapped = %d\n", t->tht_ch ? 1 : 0);
|
||||
|
||||
psi_save_transport(fp, t);
|
||||
|
||||
fprintf(fp, "}\n");
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load config for the given mux
|
||||
*/
|
||||
static void
|
||||
dvb_tdmi_load(th_dvb_mux_instance_t *tdmi)
|
||||
dvb_transport_save(th_transport_t *t)
|
||||
{
|
||||
struct config_head cl;
|
||||
config_entry_t *ce;
|
||||
char buf[400];
|
||||
const char *v;
|
||||
int sid, pmt;
|
||||
th_transport_t *t;
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/dvbmuxes/%s",
|
||||
settings_dir, tdmi->tdmi_identifier);
|
||||
|
||||
TAILQ_INIT(&cl);
|
||||
config_read_file0(buf, &cl);
|
||||
htsmsg_add_u32(m, "service_id", t->tht_dvb_service_id);
|
||||
htsmsg_add_u32(m, "pmt", t->tht_pmt);
|
||||
htsmsg_add_u32(m, "stype", t->tht_servicetype);
|
||||
htsmsg_add_u32(m, "scrambled", t->tht_scrambled);
|
||||
|
||||
TAILQ_FOREACH(ce, &cl, ce_link) {
|
||||
if(ce->ce_type != CFG_SUB || strcasecmp("service", ce->ce_key))
|
||||
continue;
|
||||
if(t->tht_provider != NULL)
|
||||
htsmsg_add_str(m, "provider", t->tht_provider);
|
||||
|
||||
sid = atoi(config_get_str_sub(&ce->ce_sub, "service_id", "0"));
|
||||
pmt = atoi(config_get_str_sub(&ce->ce_sub, "pmt", "0"));
|
||||
if(sid < 1 || pmt < 1)
|
||||
continue;
|
||||
|
||||
t = dvb_find_transport(tdmi, sid, pmt, NULL);
|
||||
|
||||
t->tht_servicetype = atoi(config_get_str_sub(&ce->ce_sub, "stype", "0"));
|
||||
t->tht_scrambled = atoi(config_get_str_sub(&ce->ce_sub, "scrambled", "0"));
|
||||
if(t->tht_svcname != NULL)
|
||||
htsmsg_add_str(m, "servicename", t->tht_svcname);
|
||||
|
||||
v = config_get_str_sub(&ce->ce_sub, "provider", "unknown");
|
||||
free((void *)t->tht_provider);
|
||||
t->tht_provider = strdup(v);
|
||||
if(t->tht_chname != NULL)
|
||||
htsmsg_add_str(m, "channelname", t->tht_chname);
|
||||
|
||||
v = config_get_str_sub(&ce->ce_sub, "servicename", "unknown");
|
||||
free((void *)t->tht_svcname);
|
||||
t->tht_svcname = strdup(v);
|
||||
|
||||
v = config_get_str_sub(&ce->ce_sub, "channelname", NULL);
|
||||
if(v != NULL) {
|
||||
free((void *)t->tht_chname);
|
||||
t->tht_chname = strdup(v);
|
||||
} else {
|
||||
t->tht_chname = strdup(t->tht_svcname);
|
||||
}
|
||||
|
||||
psi_load_transport(&ce->ce_sub, t);
|
||||
|
||||
if(atoi(config_get_str_sub(&ce->ce_sub, "mapped", "0"))) {
|
||||
transport_map_channel(t, NULL);
|
||||
}
|
||||
}
|
||||
config_free0(&cl);
|
||||
htsmsg_add_u32(m, "mapped", !!t->tht_ch);
|
||||
|
||||
psi_get_transport_settings(m, t);
|
||||
|
||||
hts_settings_save(m, "dvbtransports/%s", t->tht_identifier);
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when config changes for the given transport
|
||||
*/
|
||||
static void
|
||||
dvb_transport_config_change(th_transport_t *t)
|
||||
{
|
||||
th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
|
||||
|
||||
dvb_tdmi_save(tdmi);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Called to get quality for the given transport
|
||||
|
@ -762,7 +699,8 @@ dvb_tda_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src)
|
|||
tdmi_src->tdmi_switchport,
|
||||
tdmi_src->tdmi_transport_stream_id,
|
||||
tdmi_src->tdmi_network,
|
||||
0);
|
||||
"copy operation");
|
||||
|
||||
|
||||
assert(tdmi_dst != NULL);
|
||||
|
||||
|
|
8
dvb.h
8
dvb.h
|
@ -50,18 +50,12 @@ 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 sid, int pmt_pid, int *created);
|
||||
|
||||
#define DVB_MUX_SAVE 0x1
|
||||
#define DVB_MUX_LOAD 0x2
|
||||
|
||||
th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda,
|
||||
struct dvb_frontend_parameters *fe_param,
|
||||
int polarisation, int switchport,
|
||||
uint16_t tsid, const char *network,
|
||||
int flags);
|
||||
const char *logprefix);
|
||||
|
||||
void dvb_tdmi_save(th_dvb_mux_instance_t *tdmi);
|
||||
|
||||
void dvb_tda_save(th_dvb_adapter_t *tda);
|
||||
|
||||
void dvb_mux_unref(th_dvb_mux_instance_t *tdmi);
|
||||
|
||||
|
|
|
@ -52,7 +52,8 @@ dvb_dvr_init(th_dvb_adapter_t *tda)
|
|||
|
||||
dvr = open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK);
|
||||
if(dvr == -1) {
|
||||
syslog(LOG_ALERT, "%s: unable to open dvr\n", tda->tda_dvr_path);
|
||||
tvhlog(LOG_ALERT, "dvb",
|
||||
"%s: unable to open dvr\n", tda->tda_dvr_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -179,7 +180,7 @@ dvb_start_feed(th_transport_t *t, unsigned int weight, int status,
|
|||
|
||||
if(fd == -1) {
|
||||
st->st_demuxer_fd = -1;
|
||||
syslog(LOG_ERR,
|
||||
tvhlog(LOG_ERR, "dvb",
|
||||
"\"%s\" unable to open demuxer \"%s\" for pid %d -- %s",
|
||||
t->tht_name, tda->tda_demux_path, pid, strerror(errno));
|
||||
continue;
|
||||
|
@ -193,7 +194,7 @@ dvb_start_feed(th_transport_t *t, unsigned int weight, int status,
|
|||
dmx_param.flags = DMX_IMMEDIATE_START;
|
||||
|
||||
if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) {
|
||||
syslog(LOG_ERR,
|
||||
tvhlog(LOG_ERR, "dvb",
|
||||
"\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s",
|
||||
t->tht_name, tda->tda_demux_path, pid, strerror(errno));
|
||||
close(fd);
|
||||
|
|
8
dvb_fe.c
8
dvb_fe.c
|
@ -33,6 +33,7 @@
|
|||
#include <linux/dvb/dmx.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dispatch.h"
|
||||
|
@ -217,7 +218,7 @@ tdmi_stop(th_dvb_mux_instance_t *tdmi)
|
|||
pthread_mutex_unlock(&tdmi->tdmi_table_lock);
|
||||
|
||||
tdmi->tdmi_state = TDMI_IDLE;
|
||||
notify_tdmi_state_change(tdmi);
|
||||
// notify_tdmi_state_change(tdmi);
|
||||
time(&tdmi->tdmi_lost_adapter);
|
||||
}
|
||||
|
||||
|
@ -234,7 +235,7 @@ dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, tdmi_state_t state)
|
|||
|
||||
if(tdmi->tdmi_state != state) {
|
||||
tdmi->tdmi_state = state;
|
||||
notify_tdmi_state_change(tdmi);
|
||||
// notify_tdmi_state_change(tdmi);
|
||||
}
|
||||
|
||||
if(tda->tda_mux_current == tdmi)
|
||||
|
@ -247,7 +248,8 @@ dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, tdmi_state_t state)
|
|||
|
||||
if(maylog) {
|
||||
dvb_mux_nicename(buf, sizeof(buf), tdmi);
|
||||
syslog(LOG_DEBUG, "\"%s\" tuning to mux \"%s\"", tda->tda_rootpath, buf);
|
||||
tvhlog(LOG_DEBUG, "dvb",
|
||||
"\"%s\" tuning to mux \"%s\"", tda->tda_rootpath, buf);
|
||||
}
|
||||
/* Add tables which will be activated once the tuning is completed */
|
||||
|
||||
|
|
349
dvb_muxconfig.c
349
dvb_muxconfig.c
|
@ -23,6 +23,8 @@
|
|||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <libhts/htssettings.h>
|
||||
|
||||
#include <linux/dvb/frontend.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
|
@ -32,6 +34,58 @@
|
|||
#include "dvb_muxconfig.h"
|
||||
#include "dvb_support.h"
|
||||
#include "transports.h"
|
||||
#include "notify.h"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Save config for the given adapter
|
||||
*/
|
||||
void
|
||||
dvb_tda_save(th_dvb_adapter_t *tda)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
|
||||
htsmsg_add_str(m, "type", dvb_adaptertype_to_str(tda->tda_type));
|
||||
htsmsg_add_str(m, "displayname", tda->tda_displayname);
|
||||
hts_settings_save(m, "dvbadapters/%s", tda->tda_identifier);
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
dvb_tda_createmsg(th_dvb_adapter_t *tda)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
htsmsg_add_str(m, "id", tda->tda_identifier);
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvb_tda_set_displayname(th_dvb_adapter_t *tda, const char *s)
|
||||
{
|
||||
htsmsg_t *m = dvb_tda_createmsg(tda);
|
||||
|
||||
free(tda->tda_displayname);
|
||||
tda->tda_displayname = strdup(s);
|
||||
|
||||
dvb_tda_save(tda);
|
||||
|
||||
htsmsg_add_str(m, "name", tda->tda_displayname);
|
||||
|
||||
notify_by_msg("dvbadapter", m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static struct strtab fectab[] = {
|
||||
{ "NONE", FEC_NONE },
|
||||
|
@ -93,158 +147,165 @@ static struct strtab poltab[] = {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvb_mux_store(FILE *fp, th_dvb_mux_instance_t *tdmi)
|
||||
dvb_tdmi_save(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
struct dvb_frontend_parameters *f = &tdmi->tdmi_fe_params;
|
||||
|
||||
fprintf(fp, "\ttransportstreamid = %d\n", tdmi->tdmi_transport_stream_id);
|
||||
if(tdmi->tdmi_network != NULL)
|
||||
fprintf(fp, "\tnetwork = %s\n", tdmi->tdmi_network);
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
|
||||
htsmsg_add_u32(m, "transportstreamid", tdmi->tdmi_transport_stream_id);
|
||||
if(tdmi->tdmi_network != NULL)
|
||||
htsmsg_add_str(m, "network", tdmi->tdmi_network);
|
||||
|
||||
htsmsg_add_u32(m, "frequency", f->frequency);
|
||||
|
||||
fprintf(fp, "\tfrequency = %d\n", f->frequency);
|
||||
|
||||
switch(tdmi->tdmi_adapter->tda_type) {
|
||||
case FE_OFDM:
|
||||
fprintf(fp, "\tbandwidth = %s\n",
|
||||
val2str(f->u.ofdm.bandwidth, bwtab));
|
||||
htsmsg_add_str(m, "bandwidth",
|
||||
val2str(f->u.ofdm.bandwidth, bwtab));
|
||||
|
||||
fprintf(fp, "\tconstellation = %s\n",
|
||||
htsmsg_add_str(m, "constellation",
|
||||
val2str(f->u.ofdm.constellation, qamtab));
|
||||
|
||||
fprintf(fp, "\ttransmission_mode = %s\n",
|
||||
htsmsg_add_str(m, "transmission_mode",
|
||||
val2str(f->u.ofdm.transmission_mode, modetab));
|
||||
|
||||
fprintf(fp, "\tguard_interval = %s\n",
|
||||
htsmsg_add_str(m, "guard_interval",
|
||||
val2str(f->u.ofdm.guard_interval, guardtab));
|
||||
|
||||
fprintf(fp, "\thierarchy = %s\n",
|
||||
htsmsg_add_str(m, "hierarchy",
|
||||
val2str(f->u.ofdm.hierarchy_information, hiertab));
|
||||
|
||||
fprintf(fp, "\tfec_hi = %s\n",
|
||||
htsmsg_add_str(m, "fec_hi",
|
||||
val2str(f->u.ofdm.code_rate_HP, fectab));
|
||||
|
||||
fprintf(fp, "\tfec_lo = %s\n",
|
||||
htsmsg_add_str(m, "fec_lo",
|
||||
val2str(f->u.ofdm.code_rate_LP, fectab));
|
||||
break;
|
||||
|
||||
case FE_QPSK:
|
||||
fprintf(fp, "\tsymbol_rate = %d\n", f->u.qpsk.symbol_rate);
|
||||
htsmsg_add_u32(m, "symbol_rate", f->u.qpsk.symbol_rate);
|
||||
|
||||
fprintf(fp, "\tfec = %s\n",
|
||||
htsmsg_add_str(m, "fec",
|
||||
val2str(f->u.qpsk.fec_inner, fectab));
|
||||
|
||||
fprintf(fp, "\tpolarisation = %s\n",
|
||||
htsmsg_add_str(m, "polarisation",
|
||||
val2str(tdmi->tdmi_polarisation, poltab));
|
||||
|
||||
fprintf(fp, "\tswitchport = %d\n", tdmi->tdmi_switchport);
|
||||
htsmsg_add_u32(m, "switchport", tdmi->tdmi_switchport);
|
||||
break;
|
||||
|
||||
case FE_QAM:
|
||||
fprintf(fp, "\tsymbol_rate = %d\n", f->u.qam.symbol_rate);
|
||||
htsmsg_add_u32(m, "symbol_rate", f->u.qam.symbol_rate);
|
||||
|
||||
fprintf(fp, "\tfec = %s\n",
|
||||
htsmsg_add_str(m, "fec",
|
||||
val2str(f->u.qam.fec_inner, fectab));
|
||||
|
||||
fprintf(fp, "\tconstellation = %s\n",
|
||||
htsmsg_add_str(m, "constellation",
|
||||
val2str(f->u.qam.modulation, qamtab));
|
||||
break;
|
||||
|
||||
case FE_ATSC:
|
||||
break;
|
||||
}
|
||||
|
||||
hts_settings_save(m, "dvbmuxes/%s/%s",
|
||||
tdmi->tdmi_adapter->tda_identifier, tdmi->tdmi_identifier);
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const char *
|
||||
dvb_mux_create_str(th_dvb_adapter_t *tda,
|
||||
const char *tsidstr,
|
||||
const char *network,
|
||||
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)
|
||||
static const char *
|
||||
dvb_tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m)
|
||||
{
|
||||
struct dvb_frontend_parameters f;
|
||||
const char *s;
|
||||
int r;
|
||||
int polarisation = 0, switchport = 0;
|
||||
|
||||
int polarisation = 0;
|
||||
unsigned int switchport = 0;
|
||||
unsigned int tsid;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
|
||||
f.inversion = INVERSION_AUTO;
|
||||
htsmsg_get_u32(m, "frequency", &f.frequency);
|
||||
|
||||
f.frequency = freqstr ? atoi(freqstr) : 0;
|
||||
if(f.frequency == 0)
|
||||
return "Invalid frequency";
|
||||
|
||||
switch(tda->tda_type) {
|
||||
case FE_OFDM:
|
||||
if(bwstr == NULL || (r = str2val(bwstr, bwtab)) < 0)
|
||||
s = htsmsg_get_str(m, "bandwidth");
|
||||
if(s == NULL || (r = str2val(s, bwtab)) < 0)
|
||||
return "Invalid bandwidth";
|
||||
f.u.ofdm.bandwidth = r;
|
||||
|
||||
if(qamstr == NULL || (r = str2val(qamstr, qamtab)) < 0)
|
||||
s = htsmsg_get_str(m, "constellation");
|
||||
if(s == NULL || (r = str2val(s, qamtab)) < 0)
|
||||
return "Invalid QAM constellation";
|
||||
f.u.ofdm.constellation = r;
|
||||
|
||||
if(tmodestr == NULL || (r = str2val(tmodestr, modetab)) < 0)
|
||||
s = htsmsg_get_str(m, "transmission_mode");
|
||||
if(s == NULL || (r = str2val(s, modetab)) < 0)
|
||||
return "Invalid transmission mode";
|
||||
f.u.ofdm.transmission_mode = r;
|
||||
|
||||
if(guardstr == NULL || (r = str2val(guardstr, guardtab)) < 0)
|
||||
s = htsmsg_get_str(m, "guard_interval");
|
||||
if(s == NULL || (r = str2val(s, guardtab)) < 0)
|
||||
return "Invalid guard interval";
|
||||
f.u.ofdm.guard_interval = r;
|
||||
|
||||
if(hierstr == NULL || (r = str2val(hierstr, hiertab)) < 0)
|
||||
s = htsmsg_get_str(m, "hierarchy");
|
||||
if(s == NULL || (r = str2val(s, 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);
|
||||
s = htsmsg_get_str(m, "fec_hi");
|
||||
if(s == NULL || (r = str2val(s, fectab)) < 0)
|
||||
return "Invalid hi-FEC";
|
||||
f.u.ofdm.code_rate_HP = r;
|
||||
|
||||
if(feclostr == NULL || (r = str2val(feclostr, fectab)) < 0)
|
||||
s = htsmsg_get_str(m, "fec_lo");
|
||||
if(s == NULL || (r = str2val(s, 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;
|
||||
htsmsg_get_u32(m, "symbol_rate", &f.u.qpsk.symbol_rate);
|
||||
if(f.u.qpsk.symbol_rate == 0)
|
||||
return "Invalid symbol rate";
|
||||
|
||||
if(fecstr == NULL || (r = str2val(fecstr, fectab)) < 0)
|
||||
s = htsmsg_get_str(m, "fec");
|
||||
if(s == NULL || (r = str2val(s, fectab)) < 0)
|
||||
return "Invalid FEC";
|
||||
f.u.qpsk.fec_inner = r;
|
||||
|
||||
if(polstr == NULL || (r = str2val(polstr, poltab)) < 0)
|
||||
s = htsmsg_get_str(m, "polarisation");
|
||||
if(s == NULL || (r = str2val(s, poltab)) < 0)
|
||||
return "Invalid polarisation";
|
||||
polarisation = r;
|
||||
|
||||
switchport = atoi(switchportstr ?: "0");
|
||||
htsmsg_get_u32(m, "switchport", &switchport);
|
||||
break;
|
||||
|
||||
case FE_QAM:
|
||||
f.u.qam.symbol_rate = symratestr ? atoi(symratestr) : 0;
|
||||
htsmsg_get_u32(m, "symbol_rate", &f.u.qam.symbol_rate);
|
||||
if(f.u.qam.symbol_rate == 0)
|
||||
return "Invalid symbol rate";
|
||||
|
||||
if(qamstr == NULL || (r = str2val(qamstr, qamtab)) < 0)
|
||||
s = htsmsg_get_str(m, "constellation");
|
||||
if(s == NULL || (r = str2val(s, qamtab)) < 0)
|
||||
return "Invalid QAM constellation";
|
||||
f.u.qam.modulation = r;
|
||||
|
||||
if(fecstr == NULL || (r = str2val(fecstr, fectab)) < 0)
|
||||
s = htsmsg_get_str(m, "fec");
|
||||
if(s == NULL || (r = str2val(s, fectab)) < 0)
|
||||
return "Invalid FEC";
|
||||
f.u.qam.fec_inner = r;
|
||||
break;
|
||||
|
@ -253,45 +314,60 @@ dvb_mux_create_str(th_dvb_adapter_t *tda,
|
|||
break;
|
||||
}
|
||||
|
||||
dvb_mux_create(tda, &f, polarisation, switchport, atoi(tsidstr),
|
||||
network, save ? DVB_MUX_SAVE : DVB_MUX_LOAD);
|
||||
if(htsmsg_get_u32(m, "transportstreamid", &tsid))
|
||||
tsid = 0xffff;
|
||||
|
||||
dvb_mux_create(tda, &f, polarisation, switchport,
|
||||
tsid, htsmsg_get_str(m, "network"), NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include "linuxtv_muxes.h"
|
||||
|
||||
int
|
||||
dvb_mux_preconf_get(unsigned int n, const char **namep, const char **commentp)
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvb_tdmi_load(th_dvb_adapter_t *tda)
|
||||
{
|
||||
if(n >= sizeof(networks) / sizeof(networks[0]))
|
||||
return -1;
|
||||
htsmsg_t *l, *c;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
if(namep != NULL)
|
||||
*namep = networks[n].name;
|
||||
|
||||
if(commentp != NULL)
|
||||
*commentp = networks[n].comment;
|
||||
|
||||
return networks[n].type;
|
||||
if((l = hts_settings_load("dvbmuxes/%s", tda->tda_identifier)) == NULL)
|
||||
return;
|
||||
|
||||
HTSMSG_FOREACH(f, l) {
|
||||
if((c = htsmsg_get_msg_by_field(f)) == NULL)
|
||||
continue;
|
||||
|
||||
dvb_tdmi_create_by_msg(tda, c);
|
||||
}
|
||||
htsmsg_destroy(l);
|
||||
}
|
||||
|
||||
int
|
||||
dvb_mux_preconf_add(th_dvb_adapter_t *tda, unsigned int n)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A big list of all known DVB networks (from linuxtv.org)
|
||||
*/
|
||||
#include "linuxtv_muxes.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvb_mux_preconf_add(th_dvb_adapter_t *tda, const struct mux *m, int num,
|
||||
const char *source)
|
||||
{
|
||||
struct dvb_frontend_parameters f;
|
||||
struct mux *m;
|
||||
int i;
|
||||
int polarisation, switchport;
|
||||
int polarisation;
|
||||
int switchport;
|
||||
|
||||
if(n >= sizeof(networks) / sizeof(networks[0]))
|
||||
return -1;
|
||||
printf("m = %p, num = %d\n", m, num);
|
||||
|
||||
m = networks[n].muxes;
|
||||
|
||||
for(i = 0; i < networks[n].nmuxes; i++) {
|
||||
for(i = 0; i < num; i++) {
|
||||
|
||||
polarisation = 0;
|
||||
switchport = 0;
|
||||
|
@ -337,9 +413,114 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, unsigned int n)
|
|||
}
|
||||
|
||||
dvb_mux_create(tda, &f, polarisation, switchport, 0xffff, NULL,
|
||||
DVB_MUX_LOAD);
|
||||
source);
|
||||
m++;
|
||||
}
|
||||
dvb_tda_save(tda);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
int
|
||||
dvb_mux_preconf_add_network(th_dvb_adapter_t *tda, const char *id)
|
||||
{
|
||||
const struct region *r;
|
||||
const struct network *n;
|
||||
int nr, nn, i, j;
|
||||
char source[100];
|
||||
|
||||
snprintf(source, sizeof(source), "built-in configuration from \"%s\"", id);
|
||||
|
||||
switch(tda->tda_type) {
|
||||
case FE_QAM:
|
||||
r = regions_DVBC;
|
||||
nr = sizeof(regions_DVBC) / sizeof(regions_DVBC[0]);
|
||||
break;
|
||||
case FE_QPSK:
|
||||
r = regions_DVBS;
|
||||
nr = sizeof(regions_DVBS) / sizeof(regions_DVBS[0]);
|
||||
break;
|
||||
case FE_OFDM:
|
||||
r = regions_DVBT;
|
||||
nr = sizeof(regions_DVBT) / sizeof(regions_DVBT[0]);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(i = 0; i < nr; i++) {
|
||||
n = r[i].networks;
|
||||
nn = r[i].nnetworks;
|
||||
|
||||
for(j = 0; j < nn; j++) {
|
||||
if(!strcmp(n[j].name, id)) {
|
||||
dvb_mux_preconf_add(tda, n[j].muxes, n[j].nmuxes, source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
htsmsg_t *
|
||||
dvb_mux_preconf_get_node(int fetype, const char *node)
|
||||
{
|
||||
const struct region *r;
|
||||
const struct network *n;
|
||||
int nr, nn, i;
|
||||
htsmsg_t *out, *e;
|
||||
|
||||
switch(fetype) {
|
||||
case FE_QAM:
|
||||
r = regions_DVBC;
|
||||
nr = sizeof(regions_DVBC) / sizeof(regions_DVBC[0]);
|
||||
break;
|
||||
case FE_QPSK:
|
||||
r = regions_DVBS;
|
||||
nr = sizeof(regions_DVBS) / sizeof(regions_DVBS[0]);
|
||||
break;
|
||||
case FE_OFDM:
|
||||
r = regions_DVBT;
|
||||
nr = sizeof(regions_DVBT) / sizeof(regions_DVBT[0]);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out = htsmsg_create_array();
|
||||
|
||||
if(!strcmp(node, "root")) {
|
||||
|
||||
for(i = 0; i < nr; i++) {
|
||||
e = htsmsg_create();
|
||||
htsmsg_add_u32(e, "leaf", 0);
|
||||
htsmsg_add_str(e, "text", r[i].name);
|
||||
htsmsg_add_str(e, "id", r[i].name);
|
||||
htsmsg_add_msg(out, NULL, e);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
for(i = 0; i < nr; i++)
|
||||
if(!strcmp(node, r[i].name))
|
||||
break;
|
||||
|
||||
if(i == nr)
|
||||
return out;
|
||||
n = r[i].networks;
|
||||
nn = r[i].nnetworks;
|
||||
|
||||
for(i = 0; i < nn; i++) {
|
||||
e = htsmsg_create();
|
||||
htsmsg_add_u32(e, "leaf", 1);
|
||||
htsmsg_add_str(e, "text", n[i].name);
|
||||
htsmsg_add_str(e, "id", n[i].name);
|
||||
htsmsg_add_msg(out, NULL, e);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,13 @@
|
|||
#ifndef DVB_MUXCONFIG_H_
|
||||
#define DVB_MUXCONFIG_H_
|
||||
|
||||
void dvb_mux_store(FILE *fp, th_dvb_mux_instance_t *tdmi);
|
||||
#include <libhts/htsmsg.h>
|
||||
|
||||
void dvb_tda_save(th_dvb_adapter_t *tda);
|
||||
|
||||
void dvb_tda_set_displayname(th_dvb_adapter_t *tda, const char *s);
|
||||
|
||||
void dvb_tdmi_save(th_dvb_mux_instance_t *tdmi);
|
||||
|
||||
const char *dvb_mux_create_str(th_dvb_adapter_t *tda,
|
||||
const char *tsidstr,
|
||||
|
@ -38,9 +44,10 @@ const char *dvb_mux_create_str(th_dvb_adapter_t *tda,
|
|||
const char *switchportstr,
|
||||
int save);
|
||||
|
||||
int dvb_mux_preconf_get(unsigned int n, const char **namep,
|
||||
const char **commentp);
|
||||
htsmsg_t *dvb_mux_preconf_get_node(int fetype, const char *node);
|
||||
|
||||
int dvb_mux_preconf_add(th_dvb_adapter_t *tda, unsigned int n);
|
||||
int dvb_mux_preconf_add_network(th_dvb_adapter_t *tda, const char *id);
|
||||
|
||||
void dvb_tdmi_load(th_dvb_adapter_t *tda);
|
||||
|
||||
#endif /* DVB_MUXCONFIG_H */
|
||||
|
|
|
@ -292,18 +292,45 @@ dvb_mux_status(th_dvb_mux_instance_t *tdmi, int nullisok)
|
|||
return txt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static const char *
|
||||
nicenum(char *x, size_t siz, unsigned int v)
|
||||
{
|
||||
if(v < 1000)
|
||||
snprintf(x, siz, "%d", v);
|
||||
else if(v < 1000000)
|
||||
snprintf(x, siz, "%d,%03d", v / 1000, v % 1000);
|
||||
else if(v < 1000000000)
|
||||
snprintf(x, siz, "%d,%03d,%03d",
|
||||
v / 1000000, (v % 1000000) / 1000, v % 1000);
|
||||
else
|
||||
snprintf(x, siz, "%d,%03d,%03d,%03d",
|
||||
v / 1000000000, (v % 1000000000) / 1000000,
|
||||
(v % 1000000) / 1000, v % 1000);
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvb_mux_nicename(char *buf, size_t size, th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
if(tdmi->tdmi_adapter->tda_type == FE_QPSK)
|
||||
snprintf(buf, size, "%dkHz %s port %d", tdmi->tdmi_fe_params.frequency,
|
||||
char freq[50];
|
||||
|
||||
|
||||
if(tdmi->tdmi_adapter->tda_type == FE_QPSK) {
|
||||
nicenum(freq, sizeof(freq), tdmi->tdmi_fe_params.frequency);
|
||||
snprintf(buf, size, "%s kHz %s port %d", freq,
|
||||
dvb_polarisation_to_str_long(tdmi->tdmi_polarisation),
|
||||
tdmi->tdmi_switchport);
|
||||
else
|
||||
snprintf(buf, size, "%dHz", tdmi->tdmi_fe_params.frequency);
|
||||
} else {
|
||||
nicenum(freq, sizeof(freq), tdmi->tdmi_fe_params.frequency / 1000);
|
||||
snprintf(buf, size, "%s kHz", freq);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
12
dvb_tables.c
12
dvb_tables.c
|
@ -36,6 +36,7 @@
|
|||
#include "tvhead.h"
|
||||
#include "dispatch.h"
|
||||
#include "dvb.h"
|
||||
#include "dvb_muxconfig.h"
|
||||
#include "dvb_support.h"
|
||||
#include "epg.h"
|
||||
#include "transports.h"
|
||||
|
@ -438,7 +439,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
}
|
||||
if(change) {
|
||||
dvb_tdmi_save(tdmi);
|
||||
notify_tdmi_services_change(tdmi);
|
||||
// notify_tdmi_services_change(tdmi);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,7 +568,7 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
fe_param.u.qam.fec_inner = fec_tab[ptr[10] & 0x07];
|
||||
|
||||
dvb_mux_create(tdmi->tdmi_adapter, &fe_param, 0, 0, tsid, NULL,
|
||||
DVB_MUX_SAVE);
|
||||
"automatic mux discovery");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -601,7 +602,8 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
pol = (ptr[6] >> 5) & 0x03;
|
||||
|
||||
dvb_mux_create(tdmi->tdmi_adapter, &fe_param, pol, tdmi->tdmi_switchport,
|
||||
tsid, NULL, DVB_MUX_SAVE);
|
||||
tsid, NULL,
|
||||
"automatic mux discovery");
|
||||
}
|
||||
|
||||
|
||||
|
@ -644,7 +646,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
if(strcmp(tdmi->tdmi_network ?: "", networkname)) {
|
||||
free((void *)tdmi->tdmi_network);
|
||||
tdmi->tdmi_network = strdup(networkname);
|
||||
notify_tdmi_name_change(tdmi);
|
||||
//notify_tdmi_name_change(tdmi);
|
||||
dvb_tda_save(tdmi->tdmi_adapter);
|
||||
}
|
||||
break;
|
||||
|
@ -712,7 +714,7 @@ dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
v ^= t->tht_pmt_seen;
|
||||
if(v) {
|
||||
dvb_tdmi_save(tdmi);
|
||||
notify_tdmi_services_change(tdmi);
|
||||
//notify_tdmi_services_change(tdmi);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <libxml/tree.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "channels.h"
|
||||
|
@ -304,7 +305,7 @@ xmltv_transfer_events(xmltv_grabber_t *xg)
|
|||
how = 1;
|
||||
}
|
||||
if(strcmp(xc->xc_bestmatch ?: "", ch->ch_name)) {
|
||||
syslog(LOG_DEBUG,
|
||||
tvhlog(LOG_DEBUG, "xmltv",
|
||||
"xmltv: mapped \"%s\" (%s) to \"%s\" by %s",
|
||||
xc->xc_displayname, xc->xc_name, ch->ch_name,
|
||||
how ? "consequtive-event-matching" : "name");
|
||||
|
@ -608,7 +609,7 @@ icom_cb(void *opaque, htsmsg_t *m)
|
|||
xg->xg_status = v;
|
||||
dtimer_arm(&xg->xg_grab_timer, regrab, xg, v == XMLTV_GRAB_OK ? 3600 : 60);
|
||||
dtimer_arm(&xg->xg_xfer_timer, xmltv_xfer, xg, 1);
|
||||
notify_xmltv_grabber_status_change(xg);
|
||||
// notify_xmltv_grabber_status_change(xg);
|
||||
}
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/avstring.h>
|
||||
|
@ -94,7 +95,8 @@ tffm_set_state(th_ffmuxer_t *tffm, int status)
|
|||
break;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "%s - Entering state \"%s\"", tffm->tffm_printname, tp);
|
||||
syslog(LOG_INFO,
|
||||
"%s - Entering state \"%s\"", tffm->tffm_printname, tp);
|
||||
tffm->tffm_state = status;
|
||||
}
|
||||
|
||||
|
@ -151,7 +153,7 @@ tffm_open(th_ffmuxer_t *tffm, th_transport_t *t,
|
|||
|
||||
codec = avcodec_find_decoder(codec_id);
|
||||
if(codec == NULL) {
|
||||
syslog(LOG_ERR,
|
||||
tvhlog(LOG_ERR,
|
||||
"%s - Cannot find codec for %s, ignoring stream",
|
||||
printname, codec_name);
|
||||
continue;
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
|
|
|
@ -274,7 +274,8 @@ cr_streamport(client_t *c, char **argv, int argc)
|
|||
c->c_ipaddr.s_addr = inet_addr(argv[0]);
|
||||
c->c_port = atoi(argv[1]);
|
||||
|
||||
syslog(LOG_INFO, "%s registers UDP stream target %s:%d",
|
||||
tvhlog(LOG_INFO, "htsclient",
|
||||
"%s registers UDP stream target %s:%d",
|
||||
tcp_logname(&c->c_tcp_session), inet_ntoa(c->c_ipaddr), c->c_port);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <libhts/htscfg.h>
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "iptv_output.h"
|
||||
|
|
11681
linuxtv_muxes.h
11681
linuxtv_muxes.h
File diff suppressed because it is too large
Load diff
46
main.c
46
main.c
|
@ -26,6 +26,7 @@
|
|||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
@ -59,12 +60,14 @@
|
|||
#include "spawn.h"
|
||||
#include "ffmuxer.h"
|
||||
#include "xbmsp.h"
|
||||
#include "ajaxui/ajaxui.h"
|
||||
//#include "ajaxui/ajaxui.h"
|
||||
#include "webui/webui.h"
|
||||
#include "access.h"
|
||||
#include "serviceprobe.h"
|
||||
|
||||
#include <libhts/htsparachute.h>
|
||||
#include <libhts/htssettings.h>
|
||||
|
||||
|
||||
int running;
|
||||
int startupcounter;
|
||||
|
@ -189,6 +192,9 @@ main(int argc, char **argv)
|
|||
|
||||
openlog("tvheadend", LOG_PID, logfacility);
|
||||
|
||||
hts_settings_init("tvheadend2");
|
||||
|
||||
|
||||
if(settingspath == NULL && (homedir = getenv("HOME")) != NULL) {
|
||||
snprintf(buf2, sizeof(buf2), "%s/.hts", homedir);
|
||||
|
||||
|
@ -273,14 +279,14 @@ main(int argc, char **argv)
|
|||
|
||||
autorec_init();
|
||||
epg_init();
|
||||
xmltv_init();
|
||||
// xmltv_init();
|
||||
|
||||
subscriptions_init();
|
||||
|
||||
// htmlui_start();
|
||||
|
||||
webui_start();
|
||||
ajaxui_start();
|
||||
// ajaxui_start();
|
||||
|
||||
avgen_init();
|
||||
|
||||
|
@ -305,7 +311,7 @@ main(int argc, char **argv)
|
|||
output_multicast_setup();
|
||||
client_start();
|
||||
|
||||
p = atoi(config_get_str("http-server-port", "9980"));
|
||||
p = atoi(config_get_str("http-server-port", "9981"));
|
||||
if(p)
|
||||
http_start(p);
|
||||
|
||||
|
@ -433,3 +439,35 @@ settings_open_for_write(const char *name)
|
|||
|
||||
return fp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
tvhlog(int severity, const char *subsys, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
htsmsg_t *m;
|
||||
char buf[512];
|
||||
int l;
|
||||
|
||||
l = snprintf(buf, sizeof(buf), "%s: ", subsys);
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf + l, sizeof(buf) - l, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
syslog(severity, "%s", buf);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
m = htsmsg_create();
|
||||
htsmsg_add_str(m, "notificationClass", "logmessage");
|
||||
htsmsg_add_str(m, "logtxt", buf);
|
||||
comet_mailbox_add_message(m);
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
|
|
@ -16,19 +16,18 @@ echo char hierarchy\;
|
|||
echo char polarisation\;
|
||||
echo }\;
|
||||
|
||||
find $1/dvb-s -type f | sort | xargs ./muxbuilder 1 FE_QPSK
|
||||
find $1/dvb-t -type f | sort | xargs ./muxbuilder 1 FE_OFDM
|
||||
find $1/dvb-c -type f | sort | xargs ./muxbuilder 1 FE_QAM
|
||||
|
||||
|
||||
echo struct {
|
||||
echo int type\;
|
||||
echo struct network {
|
||||
echo const char *name\;
|
||||
echo struct mux *muxes\;
|
||||
echo int nmuxes\;
|
||||
echo const char *comment\;
|
||||
echo } networks[] = {
|
||||
find $1/dvb-s -type f | sort | xargs ./muxbuilder 2 FE_QPSK
|
||||
find $1/dvb-t -type f | sort | xargs ./muxbuilder 2 FE_OFDM
|
||||
find $1/dvb-c -type f | sort | xargs ./muxbuilder 2 FE_QAM
|
||||
echo }\;
|
||||
echo const struct mux *muxes\;
|
||||
echo const int nmuxes\;
|
||||
echo }\;
|
||||
|
||||
echo struct region {
|
||||
echo const char *name\;
|
||||
echo const struct network *networks\;
|
||||
echo const int nnetworks\;
|
||||
echo }\;
|
||||
|
||||
find $1/dvb-s -type f | sort | xargs ./muxbuilder DVBS
|
||||
find $1/dvb-t -type f | sort | xargs ./muxbuilder DVBT
|
||||
find $1/dvb-c -type f | sort | xargs ./muxbuilder DVBC
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <strings.h>
|
||||
#include <ctype.h>
|
||||
|
||||
int pass;
|
||||
char *type;
|
||||
|
||||
struct strtab {
|
||||
|
@ -163,16 +162,143 @@ dvb_c_config(const char *l)
|
|||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
typedef struct network {
|
||||
char *structname;
|
||||
char *displayname;
|
||||
char *comment;
|
||||
struct network *next;
|
||||
} network_t;
|
||||
|
||||
|
||||
typedef struct region {
|
||||
char *shortname;
|
||||
char *longname;
|
||||
struct network *networks;
|
||||
struct region *next;
|
||||
} region_t;
|
||||
|
||||
region_t *regions;
|
||||
|
||||
|
||||
static region_t *
|
||||
find_region(const char *name, const char *longname)
|
||||
{
|
||||
region_t *c, *n, **p;
|
||||
|
||||
for(c = regions; c != NULL; c = c->next)
|
||||
if(!strcmp(name, c->shortname))
|
||||
return c;
|
||||
|
||||
n = malloc(sizeof(region_t));
|
||||
n->shortname = strdup(name);
|
||||
n->longname = strdup(longname);
|
||||
n->networks = NULL;
|
||||
|
||||
if(regions == NULL)
|
||||
regions = n;
|
||||
else {
|
||||
p = ®ions;
|
||||
while((c = *p) != NULL) {
|
||||
if(strcmp(c->longname, longname) > 0)
|
||||
break;
|
||||
p = &c->next;
|
||||
}
|
||||
n->next = *p;
|
||||
*p = n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static network_t *
|
||||
add_network(region_t *x, const char *name)
|
||||
{
|
||||
network_t *m, *n, **p;
|
||||
|
||||
n = calloc(1, sizeof(network_t));
|
||||
n->structname = strdup(name);
|
||||
|
||||
if(x->networks == NULL)
|
||||
x->networks = n;
|
||||
else {
|
||||
p = &x->networks;
|
||||
while((m = *p) != NULL) {
|
||||
if(strcmp(m->structname, name) > 0)
|
||||
break;
|
||||
p = &m->next;
|
||||
}
|
||||
n->next = *p;
|
||||
*p = n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static const struct {
|
||||
const char *code;
|
||||
const char *name;
|
||||
|
||||
} tldlist[] = {
|
||||
{"at", "Austria"},
|
||||
{"au", "Australia"},
|
||||
{"be", "Belgium"},
|
||||
{"ch", "Switzerland"},
|
||||
{"cz", "Czech Republic"},
|
||||
{"de", "Germany"},
|
||||
{"dk", "Denmark"},
|
||||
{"es", "Spain"},
|
||||
{"fi", "Finland"},
|
||||
{"fr", "France"},
|
||||
{"gr", "Greece"},
|
||||
{"hr", "Croatia"},
|
||||
{"is", "Iceland"},
|
||||
{"it", "Italy"},
|
||||
{"lu", "Luxembourg"},
|
||||
{"lv", "Latvia"},
|
||||
{"nl", "Netherlands"},
|
||||
{"no", "Norway"},
|
||||
{"nz", "New Zealand"},
|
||||
{"pl", "Poland"},
|
||||
{"se", "Sweden"},
|
||||
{"sk", "Slovakia"},
|
||||
{"tw", "Taiwan"},
|
||||
{"uk", "United Kingdom"},
|
||||
};
|
||||
|
||||
static const char *
|
||||
tldcode2longname(const char *tld)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < sizeof(tldlist) / sizeof(tldlist[0]); i++)
|
||||
if(!strcmp(tld, tldlist[i].code))
|
||||
return tldlist[i].name;
|
||||
|
||||
fprintf(stderr, "Unable to translate tld %s\n", tld);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
convert_file(char *fname)
|
||||
scan_file(char *fname)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[200];
|
||||
int l;
|
||||
char c, *s;
|
||||
char smartname[200];
|
||||
const char *bn;
|
||||
char *bn;
|
||||
int gotcomment = 0;
|
||||
region_t *co;
|
||||
network_t *ne;
|
||||
char *name, *displayname;
|
||||
|
||||
char buf[100];
|
||||
|
||||
fp = fopen(fname, "r");
|
||||
if(fp == NULL) {
|
||||
|
@ -188,10 +314,35 @@ convert_file(char *fname)
|
|||
}
|
||||
smartname[l] = 0;
|
||||
|
||||
if(pass == 1) {
|
||||
printf("struct mux muxlist_%s_%s[] = {\n", type, smartname);
|
||||
name = basename(fname);
|
||||
|
||||
if(!strcmp(type, "DVBS")) {
|
||||
displayname = name;
|
||||
co = find_region("geo", "Geosynchronous Orbit");
|
||||
|
||||
} else {
|
||||
displayname = name + 3;
|
||||
buf[0] = name[0];
|
||||
buf[1] = name[1];
|
||||
buf[2] = 0;
|
||||
co = find_region(buf, tldcode2longname(buf));
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s_%s", type, smartname);
|
||||
|
||||
printf("static const struct mux muxes_%s[] = {\n", buf);
|
||||
|
||||
ne = add_network(co, buf);
|
||||
bn = ne->displayname = strdup(displayname);
|
||||
|
||||
while(*bn) {
|
||||
if(*bn == '_') *bn = ' ';
|
||||
bn++;
|
||||
}
|
||||
|
||||
// ne->muxlistname = strdup(buf);
|
||||
|
||||
#if 0
|
||||
if(pass == 2) {
|
||||
printf("{\n");
|
||||
printf("\t.type = %s,\n", type);
|
||||
|
@ -201,6 +352,7 @@ convert_file(char *fname)
|
|||
type, smartname);
|
||||
printf("\t.comment = ");
|
||||
}
|
||||
#endif
|
||||
|
||||
while(!feof(fp)) {
|
||||
memset(line, 0, sizeof(line));
|
||||
|
@ -217,65 +369,116 @@ convert_file(char *fname)
|
|||
if(gotcomment)
|
||||
break;
|
||||
|
||||
if(pass != 2)
|
||||
break;
|
||||
s = line + 2;
|
||||
if(strstr(s, " freq "))
|
||||
break;
|
||||
|
||||
printf("\"%s\"\n", s);
|
||||
// ne->comment = strdup(s);
|
||||
gotcomment = 1;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
if(pass == 1)
|
||||
dvb_c_config(line + 1);
|
||||
dvb_c_config(line + 1);
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
if(pass == 1)
|
||||
dvb_t_config(line + 1);
|
||||
dvb_t_config(line + 1);
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if(pass == 1)
|
||||
dvb_s_config(line + 1);
|
||||
dvb_s_config(line + 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(pass == 2) {
|
||||
if(gotcomment == 0)
|
||||
printf("\"\"\n");
|
||||
printf("},\n");
|
||||
}
|
||||
|
||||
if(pass == 1) {
|
||||
printf("};\n\n");
|
||||
}
|
||||
printf("};\n\n");
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dump_networks(region_t *c)
|
||||
{
|
||||
network_t *n;
|
||||
|
||||
printf("static const struct network networks_%s_%s[] = {\n",
|
||||
type, c->shortname);
|
||||
|
||||
for(n = c->networks; n != NULL; n = n->next) {
|
||||
|
||||
printf("\t{\n");
|
||||
printf("\t\t.name = \"%s\",\n", n->displayname);
|
||||
printf("\t\t.muxes = muxes_%s,\n", n->structname);
|
||||
printf("\t\t.nmuxes = sizeof(muxes_%s) / sizeof(struct mux),\n",
|
||||
n->structname);
|
||||
if(n->comment)
|
||||
printf("\t\t.comment = \"%s\",\n", n->comment);
|
||||
printf("\t},\n");
|
||||
}
|
||||
printf("};\n\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
dump_regions(void)
|
||||
{
|
||||
region_t *r;
|
||||
|
||||
printf("static const struct region regions_%s[] = {\n", type);
|
||||
|
||||
for(r = regions; r != NULL; r = r->next) {
|
||||
|
||||
printf("\t{\n");
|
||||
printf("\t\t.name = \"%s\",\n", r->longname);
|
||||
printf("\t\t.networks = networks_%s_%s,\n", type, r->shortname);
|
||||
printf("\t\t.nnetworks = sizeof(networks_%s_%s) / sizeof(struct network),\n",
|
||||
type, r->shortname);
|
||||
printf("\t},\n");
|
||||
}
|
||||
printf("};\n\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
region_t *c;
|
||||
|
||||
if(argc < 3)
|
||||
if(argc < 2)
|
||||
return 1;
|
||||
|
||||
pass = atoi(argv[1]);
|
||||
if(pass < 1 || pass > 2)
|
||||
return 2;
|
||||
type = argv[1];
|
||||
#if 0
|
||||
printf("struct mux {\n"
|
||||
"unsigned int freq;\n"
|
||||
"unsigned int symrate;\n"
|
||||
"char fec;\n"
|
||||
"char constellation;\n"
|
||||
"char bw;\n"
|
||||
"char fechp;\n"
|
||||
"char feclp;\n"
|
||||
"char tmode;\n"
|
||||
"char guard;\n"
|
||||
"char hierarchy;\n"
|
||||
"char polarisation;\n"
|
||||
"}\n");
|
||||
#endif
|
||||
|
||||
type = argv[2];
|
||||
for(i = 2; i < argc; i++)
|
||||
scan_file(argv[i]);
|
||||
|
||||
for(i = 3; i < argc; i++) {
|
||||
convert_file(argv[i]);
|
||||
for(c = regions; c != NULL; c = c->next) {
|
||||
dump_networks(c);
|
||||
}
|
||||
dump_regions();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
62
notify.c
62
notify.c
|
@ -24,56 +24,19 @@
|
|||
|
||||
#include "tvhead.h"
|
||||
#include "notify.h"
|
||||
#include "ajaxui/ajaxui_mailbox.h"
|
||||
#include "webui/webui.h"
|
||||
|
||||
void
|
||||
notify_tdmi_state_change(th_dvb_mux_instance_t *tdmi)
|
||||
notify_by_msg(const char *class, htsmsg_t *m)
|
||||
{
|
||||
ajax_mailbox_tdmi_state_change(tdmi);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
notify_tdmi_name_change(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
ajax_mailbox_tdmi_name_change(tdmi);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
notify_tdmi_status_change(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
ajax_mailbox_tdmi_status_change(tdmi);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
notify_tdmi_qual_change(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
ajax_mailbox_tdmi_qual_change(tdmi);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
notify_tdmi_services_change(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
ajax_mailbox_tdmi_services_change(tdmi);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
notify_tda_change(th_dvb_adapter_t *tda)
|
||||
{
|
||||
ajax_mailbox_tda_change(tda);
|
||||
}
|
||||
|
||||
void
|
||||
notify_xmltv_grabber_status_change(struct xmltv_grabber *xg)
|
||||
{
|
||||
ajax_mailbox_xmltv_grabber_status_change(xg);
|
||||
htsmsg_add_str(m, "notificationClass", class);
|
||||
comet_mailbox_add_message(m);
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
notify_transprot_status_change(struct th_transport *t)
|
||||
{
|
||||
|
@ -83,12 +46,5 @@ notify_transprot_status_change(struct th_transport *t)
|
|||
if(s->ths_status_callback != NULL)
|
||||
s->ths_status_callback(s, t->tht_last_status, s->ths_opaque);
|
||||
|
||||
ajax_mailbox_transport_status_change(t);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
notify_cwc_status_change(struct cwc *cwc)
|
||||
{
|
||||
ajax_mailbox_cwc_status_change(cwc);
|
||||
// ajax_mailbox_transport_status_change(t);
|
||||
}
|
||||
|
|
22
notify.h
22
notify.h
|
@ -19,28 +19,10 @@
|
|||
#ifndef NOTIFY_H_
|
||||
#define NOTIFY_H_
|
||||
|
||||
struct xmltv_grabber;
|
||||
struct th_dvb_mux_instance;
|
||||
struct th_dvb_adapterr;
|
||||
#include <libhts/htsmsg.h>
|
||||
|
||||
void notify_tdmi_state_change(struct th_dvb_mux_instance *tdmi);
|
||||
void notify_by_msg(const char *class, htsmsg_t *m);
|
||||
|
||||
void notify_tdmi_name_change(struct th_dvb_mux_instance *tdmi);
|
||||
|
||||
void notify_tdmi_status_change(struct th_dvb_mux_instance *tdmi);
|
||||
|
||||
void notify_tdmi_qual_change(struct th_dvb_mux_instance *tdmi);
|
||||
|
||||
void notify_tdmi_services_change(struct th_dvb_mux_instance *tdmi);
|
||||
|
||||
void notify_tda_change(struct th_dvb_adapter *tda);
|
||||
|
||||
void notify_xmltv_grabber_status_change(struct xmltv_grabber *xg);
|
||||
|
||||
struct th_transport;
|
||||
void notify_transprot_status_change(struct th_transport *t);
|
||||
|
||||
struct cwc;
|
||||
void notify_cwc_status_change(struct cwc *cwc);
|
||||
|
||||
#endif /* NOTIFY_H_ */
|
||||
|
|
29
psi.c
29
psi.c
|
@ -543,38 +543,39 @@ htstvstreamtype2txt(tv_streamtype_t s)
|
|||
|
||||
|
||||
/**
|
||||
* Save transport info
|
||||
* Store transport settings into message
|
||||
*/
|
||||
void
|
||||
psi_save_transport(FILE *fp, th_transport_t *t)
|
||||
psi_get_transport_settings(htsmsg_t *m, th_transport_t *t)
|
||||
{
|
||||
th_stream_t *st;
|
||||
htsmsg_t *sub;
|
||||
|
||||
fprintf(fp, "\tpcr = %d\n", t->tht_pcr_pid);
|
||||
htsmsg_add_u32(m, "pcr", t->tht_pcr_pid);
|
||||
|
||||
if(t->tht_disabled)
|
||||
fprintf(fp, "\tdisabled = 1\n");
|
||||
htsmsg_add_u32(m, "disabled", !!t->tht_disabled);
|
||||
|
||||
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) ?: "?");
|
||||
sub = htsmsg_create();
|
||||
|
||||
htsmsg_add_u32(sub, "pid", st->st_pid);
|
||||
htsmsg_add_str(sub, "type", val2str(st->st_type, streamtypetab) ?: "?");
|
||||
|
||||
if(st->st_lang[0])
|
||||
fprintf(fp, "\t\tlanguage = %s\n", st->st_lang);
|
||||
htsmsg_add_str(sub, "language", st->st_lang);
|
||||
|
||||
if(st->st_type == HTSTV_CA)
|
||||
fprintf(fp, "\t\tcaid = %s\n", psi_caid2name(st->st_caid));
|
||||
htsmsg_add_str(sub, "caid", psi_caid2name(st->st_caid));
|
||||
|
||||
if(st->st_frame_duration)
|
||||
fprintf(fp, "\t\tframeduration = %d\n", st->st_frame_duration);
|
||||
htsmsg_add_u32(sub, "frameduration", st->st_frame_duration);
|
||||
|
||||
fprintf(fp, "\t}\n");
|
||||
htsmsg_add_msg(m, "stream", sub);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Load transport info
|
||||
*/
|
||||
|
@ -621,4 +622,4 @@ psi_load_transport(struct config_head *cl, th_transport_t *t)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
5
psi.h
5
psi.h
|
@ -19,6 +19,8 @@
|
|||
#ifndef PSI_H_
|
||||
#define PSI_H_
|
||||
|
||||
#include <libhts/htsmsg.h>
|
||||
|
||||
#define PSI_SECTION_SIZE 4096
|
||||
|
||||
typedef struct psi_section {
|
||||
|
@ -43,7 +45,6 @@ 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);
|
||||
void psi_get_transport_settings(htsmsg_t *m, th_transport_t *t);
|
||||
|
||||
#endif /* PSI_H_ */
|
||||
|
|
18
pvr.c
18
pvr.c
|
@ -513,12 +513,12 @@ pvr_generate_filename(pvr_rec_t *pvrr)
|
|||
|
||||
while(1) {
|
||||
if(stat(fullname, &st) == -1) {
|
||||
syslog(LOG_DEBUG, "pvr: File \"%s\" -- %s -- Using for recording",
|
||||
tvhlog(LOG_DEBUG, "pvr", "File \"%s\" -- %s -- Using for recording",
|
||||
fullname, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
syslog(LOG_DEBUG, "pvr: Overwrite protection, file \"%s\" exists",
|
||||
tvhlog(LOG_DEBUG, "pvr", "Overwrite protection, file \"%s\" exists",
|
||||
fullname);
|
||||
|
||||
tally++;
|
||||
|
@ -581,7 +581,7 @@ pvr_fsm(pvr_rec_t *pvrr)
|
|||
delta = pvrr->pvrr_stop - now;
|
||||
|
||||
if(delta <= 0) {
|
||||
syslog(LOG_NOTICE, "pvr: \"%s\" - Recording skipped, "
|
||||
tvhlog(LOG_NOTICE, "pvr", "\"%s\" - Recording skipped, "
|
||||
"program has already come to pass", pvrr->pvrr_printname);
|
||||
pvrr->pvrr_status = HTSTV_PVR_STATUS_DONE;
|
||||
pvr_inform_status_change(pvrr);
|
||||
|
@ -677,8 +677,8 @@ pvrr_transport_available(pvr_rec_t *pvrr, th_transport_t *t)
|
|||
|
||||
fmt = guess_format(pvrr->pvrr_fmt_lavfname, NULL, NULL);
|
||||
if(fmt == NULL) {
|
||||
syslog(LOG_ERR,
|
||||
"pvr: \"%s\" - Unable to open file format \"%s\" for output",
|
||||
tvhlog(LOG_ERR, "pvr",
|
||||
"\"%s\" - Unable to open file format \"%s\" for output",
|
||||
pvrr->pvrr_printname, pvrr->pvrr_fmt_lavfname);
|
||||
pvrr->pvrr_error = HTSTV_PVR_STATUS_FILE_ERROR;
|
||||
pvr_fsm(pvrr);
|
||||
|
@ -706,8 +706,8 @@ pvrr_transport_available(pvr_rec_t *pvrr, th_transport_t *t)
|
|||
snprintf(urlname, sizeof(urlname), "file:%s", pvrr->pvrr_filename);
|
||||
|
||||
if((err = url_fopen(&fctx->pb, urlname, URL_WRONLY)) < 0) {
|
||||
syslog(LOG_ERR,
|
||||
"pvr: \"%s\" - Unable to create output file \"%s\" -- %s\n",
|
||||
tvhlog(LOG_ERR, "pvr",
|
||||
"\"%s\" - Unable to create output file \"%s\" -- %s\n",
|
||||
pvrr->pvrr_printname, pvrr->pvrr_filename,
|
||||
strerror(AVUNERROR(err)));
|
||||
av_free(fctx);
|
||||
|
@ -800,7 +800,7 @@ pvr_recorder_thread(void *aux)
|
|||
if(t != NULL)
|
||||
*t = 0;
|
||||
|
||||
syslog(LOG_INFO, "pvr: \"%s\" - Recording started, ends at %s",
|
||||
tvhlog(LOG_INFO, "pvr", "\"%s\" - Recording started, ends at %s",
|
||||
pvrr->pvrr_printname, txt2);
|
||||
|
||||
|
||||
|
@ -845,7 +845,7 @@ pvr_recorder_thread(void *aux)
|
|||
}
|
||||
pthread_mutex_unlock(&pvrr->pvrr_pktq_mutex);
|
||||
|
||||
syslog(LOG_INFO, "pvr: \"%s\" - Recording completed",
|
||||
tvhlog(LOG_INFO, "pvr", "\"%s\" - Recording completed",
|
||||
pvrr->pvrr_printname);
|
||||
|
||||
return NULL;
|
||||
|
|
9
rtsp.c
9
rtsp.c
|
@ -255,7 +255,8 @@ rtsp_reply_error(http_connection_t *hc, int error, const char *errstr)
|
|||
if(errstr == NULL)
|
||||
errstr = rtsp_err2str(error);
|
||||
|
||||
syslog(LOG_INFO, "rtsp: %s: %s", tcp_logname(&hc->hc_tcp_session), errstr);
|
||||
tvhlog(LOG_INFO, "rtsp",
|
||||
"%s: %s", tcp_logname(&hc->hc_tcp_session), errstr);
|
||||
|
||||
http_printf(hc, "RTSP/1.0 %d %s\r\n", error, errstr);
|
||||
if((c = http_arg_get(&hc->hc_args, "cseq")) != NULL)
|
||||
|
@ -330,7 +331,8 @@ rtsp_cmd_play(http_connection_t *hc)
|
|||
ts_muxer_play(rs->rs_muxer, start);
|
||||
|
||||
if(rs->rs_s->ths_channel != NULL)
|
||||
syslog(LOG_INFO, "rtsp: %s: Starting playback of %s",
|
||||
tvhlog(LOG_INFO, "rtsp",
|
||||
"%s: Starting playback of %s",
|
||||
tcp_logname(&hc->hc_tcp_session), rs->rs_s->ths_channel->ch_name);
|
||||
|
||||
http_printf(hc,
|
||||
|
@ -367,7 +369,8 @@ rtsp_cmd_pause(http_connection_t *hc)
|
|||
ts_muxer_pause(rs->rs_muxer);
|
||||
|
||||
if(rs->rs_s->ths_channel != NULL)
|
||||
syslog(LOG_INFO, "rtsp: %s: Pausing playback of %s",
|
||||
tvhlog(LOG_INFO, "rtsp",
|
||||
"%s: Pausing playback of %s",
|
||||
tcp_logname(&hc->hc_tcp_session), rs->rs_s->ths_channel->ch_name);
|
||||
|
||||
http_printf(hc,
|
||||
|
|
|
@ -90,7 +90,7 @@ sp_packet_input(void *opaque, th_muxstream_t *tms, th_pkt_t *pkt)
|
|||
th_transport_t *t = sp->sp_t;
|
||||
channel_t *ch;
|
||||
|
||||
syslog(LOG_INFO, "Probed \"%s\" -- Ok\n", t->tht_svcname);
|
||||
tvhlog(LOG_INFO, "serviceprobe", "Probed \"%s\" -- Ok\n", t->tht_svcname);
|
||||
|
||||
if(t->tht_ch == NULL && t->tht_svcname != NULL) {
|
||||
ch = channel_find(t->tht_svcname, 1, NULL);
|
||||
|
@ -130,7 +130,8 @@ sp_status_callback(struct th_subscription *s, int status, void *opaque)
|
|||
break;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "Probed \"%s\" -- %s\n", t->tht_svcname, errtxt);
|
||||
tvhlog(LOG_INFO, "serviceprobe",
|
||||
"Probed \"%s\" -- %s\n", t->tht_svcname, errtxt);
|
||||
dtimer_arm(&sp->sp_timer, sp_done_callback, sp, 0);
|
||||
}
|
||||
|
||||
|
|
5
spawn.c
5
spawn.c
|
@ -24,6 +24,7 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dispatch.h"
|
||||
|
@ -99,7 +100,9 @@ reaper(void *opaque, int64_t now)
|
|||
"unknown status\n");
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "spawn: \"%s\" %s", s ? s->name : "<unknwon spawn>", txt);
|
||||
|
||||
tvhlog(LOG_INFO, "spawn",
|
||||
"spawn: \"%s\" %s", s ? s->name : "<unknwon spawn>", txt);
|
||||
if(s != NULL) {
|
||||
LIST_REMOVE(s, link);
|
||||
free((void *)s->name);
|
||||
|
|
|
@ -150,7 +150,8 @@ subscription_create(channel_t *ch, unsigned int weight, const char *name,
|
|||
subscription_reschedule();
|
||||
|
||||
if(s->ths_transport == NULL)
|
||||
syslog(LOG_NOTICE, "No transponder available for subscription \"%s\" "
|
||||
tvhlog(LOG_NOTICE, "subscription",
|
||||
"No transponder available for subscription \"%s\" "
|
||||
"to channel \"%s\"",
|
||||
s->ths_title, s->ths_channel->ch_name);
|
||||
|
||||
|
|
12
tcp.c
12
tcp.c
|
@ -248,7 +248,7 @@ tcp_disconnect(tcp_session_t *ses, int err)
|
|||
|
||||
ses->tcp_callback(TCP_DISCONNECT, ses);
|
||||
|
||||
syslog(LOG_INFO, "%s: %s: disconnected -- %s",
|
||||
tvhlog(LOG_INFO, "tcp", "%s: %s: disconnected -- %s",
|
||||
ses->tcp_name, ses->tcp_peer_txt, strerror(err));
|
||||
|
||||
close(dispatch_delfd(ses->tcp_dispatch_handle));
|
||||
|
@ -319,7 +319,7 @@ tcp_start_session(tcp_session_t *ses)
|
|||
snprintf(ses->tcp_peer_txt, sizeof(ses->tcp_peer_txt), "%s:%d",
|
||||
inet_ntoa(si->sin_addr), ntohs(si->sin_port));
|
||||
|
||||
syslog(LOG_INFO, "%s: %s%sConnected to %s", ses->tcp_name,
|
||||
tvhlog(LOG_INFO, "tcp", "%s: %s%sConnected to %s", ses->tcp_name,
|
||||
ses->tcp_hostname ?: "", ses->tcp_hostname ? ": " : "",
|
||||
ses->tcp_peer_txt);
|
||||
|
||||
|
@ -351,7 +351,7 @@ tcp_client_connect_fail(tcp_session_t *c, int error)
|
|||
struct sockaddr_in *si = (struct sockaddr_in *)&c->tcp_peer_addr;
|
||||
|
||||
|
||||
syslog(LOG_ERR, "%s: Unable to connect to \"%s\" (%s) : %d -- %s",
|
||||
tvhlog(LOG_ERR, "tcp", "%s: Unable to connect to \"%s\" (%s) : %d -- %s",
|
||||
c->tcp_name, c->tcp_hostname, inet_ntoa(si->sin_addr),
|
||||
ntohs(si->sin_port), strerror(error));
|
||||
|
||||
|
@ -410,7 +410,7 @@ tcp_session_peer_resolved(void *aux, struct sockaddr *so, const char *error)
|
|||
c->tcp_resolver = NULL;
|
||||
|
||||
if(error != NULL) {
|
||||
syslog(LOG_ERR, "%s: Unable to resolve \"%s\" -- %s",
|
||||
tvhlog(LOG_ERR, "tcp", "%s: Unable to resolve \"%s\" -- %s",
|
||||
c->tcp_name, c->tcp_hostname, error);
|
||||
/* Try again in 30 seconds */
|
||||
dtimer_arm(&c->tcp_timer, tcp_client_reconnect_timeout, c, 30);
|
||||
|
@ -550,11 +550,11 @@ tcp_create_server(int port, size_t session_size, const char *name,
|
|||
|
||||
fcntl(s->tcp_fd, F_SETFL, fcntl(s->tcp_fd, F_GETFL) | O_NONBLOCK);
|
||||
|
||||
syslog(LOG_INFO, "%s: Listening for TCP connections on %s:%d",
|
||||
tvhlog(LOG_INFO, "tcp", "%s: Listening for TCP connections on %s:%d",
|
||||
name, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
|
||||
|
||||
if(bind(s->tcp_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
||||
syslog(LOG_ERR,
|
||||
tvhlog(LOG_ERR, "tcp",
|
||||
"%s: Unable to bind socket for incomming TCP connections, "
|
||||
"%s:%d -- %s",
|
||||
name,
|
||||
|
|
|
@ -364,7 +364,7 @@ teletext_rundown(th_transport_t *t, channel_t *ch, tt_page_t *ttp)
|
|||
|
||||
if(prev != t->tht_tt_commercial_advice) {
|
||||
|
||||
syslog(LOG_DEBUG,
|
||||
tvhlog(LOG_DEBUG, "teletext",
|
||||
"teletext-rundown: \"%s\" on \"%s\": "
|
||||
"%s commercial break (chunk %ds)",
|
||||
ch->ch_name, t->tht_name,
|
||||
|
|
|
@ -642,7 +642,7 @@ transport_signal_status(th_transport_t *t, int newstatus)
|
|||
snprintf(buf, sizeof(buf), "\"%s\" on %s",
|
||||
t->tht_chname ?: t->tht_svcname, t->tht_sourcename(t));
|
||||
|
||||
syslog(LOG_INFO, "%s -- Changed status from \"%s\" to \"%s\"",
|
||||
tvhlog(LOG_INFO, "transport", "%s -- Changed status from \"%s\" to \"%s\"",
|
||||
buf, transport_status_to_text(t->tht_last_status),
|
||||
transport_status_to_text(newstatus));
|
||||
|
||||
|
|
12
tvhead.h
12
tvhead.h
|
@ -20,7 +20,6 @@
|
|||
#define TV_HEAD_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <syslog.h>
|
||||
#include <netinet/in.h>
|
||||
#include <libhts/htsq.h>
|
||||
#include <libhts/htstv.h>
|
||||
|
@ -957,4 +956,15 @@ static inline unsigned int tvh_strhash(const char *s, unsigned int mod)
|
|||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
void tvhlog(int severity, const char *subsys, const char *fmt, ...);
|
||||
|
||||
#define LOG_EMERG 0 /* system is unusable */
|
||||
#define LOG_ALERT 1 /* action must be taken immediately */
|
||||
#define LOG_CRIT 2 /* critical conditions */
|
||||
#define LOG_ERR 3 /* error conditions */
|
||||
#define LOG_WARNING 4 /* warning conditions */
|
||||
#define LOG_NOTICE 5 /* normal but significant condition */
|
||||
#define LOG_INFO 6 /* informational */
|
||||
#define LOG_DEBUG 7 /* debug-level messages */
|
||||
|
||||
#endif /* TV_HEAD_H */
|
||||
|
|
7
v4l.c
7
v4l.c
|
@ -157,7 +157,8 @@ v4l_setfreq(th_v4l_adapter_t *tva, int frequency)
|
|||
vf.frequency = (frequency * 16) / 1000000;
|
||||
result = ioctl(tva->tva_fd, VIDIOC_S_FREQUENCY, &vf);
|
||||
if(result < 0) {
|
||||
syslog(LOG_ERR, "%s: Unable to tune to %dHz\n", tva->tva_path, frequency);
|
||||
tvhlog(LOG_ERR, "v4l",
|
||||
"%s: Unable to tune to %dHz\n", tva->tva_path, frequency);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -165,11 +166,11 @@ v4l_setfreq(th_v4l_adapter_t *tva, int frequency)
|
|||
result = ioctl(tva->tva_fd, VIDIOC_G_TUNER, &vt);
|
||||
|
||||
if(result < 0) {
|
||||
syslog(LOG_ERR, "%s: Unable read tuner status\n", tva->tva_path);
|
||||
tvhlog(LOG_ERR, "v4l", "%s: Unable read tuner status\n", tva->tva_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
syslog(LOG_DEBUG, "%s: Tuned to %.3f MHz%s",
|
||||
tvhlog(LOG_DEBUG, "v4l", "%s: Tuned to %.3f MHz%s",
|
||||
tva->tva_path, (float)frequency/1000000.0,
|
||||
vt.signal ? " (Signal Detected)" : "");
|
||||
|
||||
|
|
190
webui/acleditor.js
Normal file
190
webui/acleditor.js
Normal file
|
@ -0,0 +1,190 @@
|
|||
|
||||
tvheadend.acleditor = function() {
|
||||
var fm = Ext.form;
|
||||
|
||||
var enabledColumn = new Ext.grid.CheckColumn({
|
||||
header: "Enabled",
|
||||
dataIndex: 'enabled',
|
||||
width: 60
|
||||
});
|
||||
|
||||
var streamingColumn = new Ext.grid.CheckColumn({
|
||||
header: "Streaming",
|
||||
dataIndex: 'streaming',
|
||||
width: 100
|
||||
});
|
||||
|
||||
var pvrColumn = new Ext.grid.CheckColumn({
|
||||
header: "Video Recorder",
|
||||
dataIndex: 'pvr',
|
||||
width: 100
|
||||
});
|
||||
|
||||
var webuiColumn = new Ext.grid.CheckColumn({
|
||||
header: "Web Interface",
|
||||
dataIndex: 'webui',
|
||||
width: 100
|
||||
});
|
||||
|
||||
var adminColumn = new Ext.grid.CheckColumn({
|
||||
header: "Admin",
|
||||
dataIndex: 'admin',
|
||||
width: 100
|
||||
});
|
||||
|
||||
var cm = new Ext.grid.ColumnModel([
|
||||
enabledColumn,
|
||||
{
|
||||
id:'username',
|
||||
header: "Username",
|
||||
dataIndex: 'username',
|
||||
editor: new fm.TextField({
|
||||
allowBlank: false
|
||||
})
|
||||
},{
|
||||
id:'password',
|
||||
header: "Password",
|
||||
dataIndex: 'password',
|
||||
editor: new fm.TextField({
|
||||
allowBlank: false
|
||||
})
|
||||
},{
|
||||
header: "Prefix",
|
||||
dataIndex: 'prefix',
|
||||
editor: new fm.TextField({
|
||||
allowBlank: false,
|
||||
})
|
||||
},
|
||||
streamingColumn,
|
||||
pvrColumn,
|
||||
webuiColumn,
|
||||
adminColumn,
|
||||
|
||||
{
|
||||
id:'comment',
|
||||
header: "Comment",
|
||||
dataIndex: 'comment',
|
||||
width: 400,
|
||||
editor: new fm.TextField({
|
||||
|
||||
})
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
cm.defaultSortable = true;
|
||||
|
||||
var UserRecord = Ext.data.Record.create([
|
||||
{name: 'enabled'},
|
||||
{name: 'streaming'},
|
||||
{name: 'pvr'},
|
||||
{name: 'admin'},
|
||||
{name: 'webui'},
|
||||
{name: 'username'},
|
||||
{name: 'prefix'},
|
||||
{name: 'password'},
|
||||
{name: 'comment'}]);
|
||||
|
||||
var store = new Ext.data.JsonStore({root: 'entries',
|
||||
fields: UserRecord,
|
||||
url: "tablemgr",
|
||||
autoLoad: true,
|
||||
id: 'id',
|
||||
storeid: 'id',
|
||||
baseParams: {table: "accesscontrol", op: "get"}
|
||||
});
|
||||
|
||||
|
||||
function addRecord() {
|
||||
Ext.Ajax.request({
|
||||
url: "tablemgr", params: {op:"create", table:"accesscontrol"},
|
||||
failure:function(response,options){
|
||||
Ext.MessageBox.alert('Server Error','Unable to generate new record');
|
||||
},
|
||||
success:function(response,options){
|
||||
var responseData = Ext.util.JSON.decode(response.responseText);
|
||||
|
||||
var p = new UserRecord(responseData, responseData.id);
|
||||
grid.stopEditing();
|
||||
store.insert(0, p);
|
||||
grid.startEditing(0, 0);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
function delSelected() {
|
||||
var selectedKeys = grid.selModel.selections.keys;
|
||||
if(selectedKeys.length > 0)
|
||||
{
|
||||
Ext.MessageBox.confirm('Message',
|
||||
'Do you really want to delete selection?',
|
||||
deleteRecord);
|
||||
} else {
|
||||
Ext.MessageBox.alert('Message',
|
||||
'Please select at least one item to delete');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function deleteRecord(btn) {
|
||||
if(btn=='yes') {
|
||||
var selectedKeys = grid.selModel.selections.keys;
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: "tablemgr", params: {op:"delete", table:"accesscontrol",
|
||||
entries:Ext.encode(selectedKeys)},
|
||||
failure:function(response,options){
|
||||
Ext.MessageBox.alert('Server Error','Unable to delete');
|
||||
},
|
||||
success:function(response,options){
|
||||
store.reload();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function saveChanges() {
|
||||
var mr = store.getModifiedRecords();
|
||||
var out = new Array();
|
||||
for (var x = 0; x < mr.length; x++) {
|
||||
v = mr[x].getChanges();
|
||||
out[x] = v;
|
||||
out[x].id = mr[x].id;
|
||||
}
|
||||
|
||||
Ext.Ajax.request({url: "tablemgr",
|
||||
params: {op:"update", table:"accesscontrol",
|
||||
entries:Ext.encode(out)},
|
||||
success:function(response,options){
|
||||
store.commitChanges();
|
||||
},
|
||||
failure:function(response,options){
|
||||
Ext.MessageBox.alert('Message',response.statusText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var grid = new Ext.grid.EditorGridPanel({
|
||||
title: 'Access Configuration',
|
||||
plugins:[enabledColumn,streamingColumn,pvrColumn,adminColumn, webuiColumn],
|
||||
store: store,
|
||||
clicksToEdit: 2,
|
||||
cm: cm,
|
||||
selModel: new Ext.grid.RowSelectionModel({singleSelect:false}),
|
||||
tbar: [{
|
||||
text: 'Add entry',
|
||||
handler: addRecord
|
||||
}, '-', {
|
||||
text: 'Delete selected',
|
||||
handler: delSelected
|
||||
}, '-', {
|
||||
text: "Save changes",
|
||||
handler: saveChanges
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return grid;
|
||||
}
|
301
webui/comet.c
Normal file
301
webui/comet.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* tvheadend, COMET
|
||||
* 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 <libavutil/md5.h>
|
||||
|
||||
|
||||
#include <libhts/htsmsg.h>
|
||||
#include <libhts/htsmsg_json.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "dispatch.h"
|
||||
#include "http.h"
|
||||
#include "webui/webui.h"
|
||||
#include "access.h"
|
||||
|
||||
#define MAILBOX_UNUSED_TIMEOUT 20
|
||||
#define MAILBOX_EMPTY_REPLY_TIMEOUT 10
|
||||
|
||||
//#define mbdebug(fmt...) printf(fmt);
|
||||
#define mbdebug(fmt...)
|
||||
|
||||
|
||||
static LIST_HEAD(, comet_mailbox) mailboxes;
|
||||
|
||||
int mailbox_tally;
|
||||
|
||||
typedef struct comet_mailbox {
|
||||
|
||||
char *cmb_boxid; /* an md5hash */
|
||||
dtimer_t cmb_timer;
|
||||
http_reply_t *cmb_hr; /* Pending request */
|
||||
|
||||
htsmsg_t *cmb_messages; /* A vector */
|
||||
|
||||
LIST_ENTRY(comet_mailbox) cmb_link;
|
||||
|
||||
} comet_mailbox_t;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
cmb_destroy(comet_mailbox_t *cmb)
|
||||
{
|
||||
mbdebug("mailbox[%s]: destroyed\n", cmb->cmb_boxid);
|
||||
|
||||
if(cmb->cmb_messages != NULL)
|
||||
htsmsg_destroy(cmb->cmb_messages);
|
||||
|
||||
LIST_REMOVE(cmb, cmb_link);
|
||||
|
||||
dtimer_disarm(&cmb->cmb_timer);
|
||||
|
||||
free(cmb->cmb_boxid);
|
||||
free(cmb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
comet_mailbox_unused(void *opaque, int64_t now)
|
||||
{
|
||||
comet_mailbox_t *cmb = opaque;
|
||||
assert(cmb->cmb_hr == NULL);
|
||||
cmb_destroy(cmb);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
comet_mailbox_connection_lost(http_reply_t *hr, void *opaque)
|
||||
{
|
||||
comet_mailbox_t *cmb = opaque;
|
||||
assert(hr == cmb->cmb_hr);
|
||||
cmb_destroy(cmb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static comet_mailbox_t *
|
||||
comet_mailbox_create(void)
|
||||
{
|
||||
comet_mailbox_t *cmb = calloc(1, sizeof(comet_mailbox_t));
|
||||
|
||||
struct timeval tv;
|
||||
uint8_t sum[16];
|
||||
char id[33];
|
||||
int i;
|
||||
struct AVMD5 *ctx;
|
||||
|
||||
ctx = alloca(av_md5_size);
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
av_md5_init(ctx);
|
||||
av_md5_update(ctx, (void *)&tv, sizeof(tv));
|
||||
av_md5_update(ctx, (void *)&mailbox_tally, sizeof(uint32_t));
|
||||
av_md5_final(ctx, sum);
|
||||
|
||||
for(i = 0; i < 16; i++) {
|
||||
id[i * 2 + 0] = "0123456789abcdef"[sum[i] >> 4];
|
||||
id[i * 2 + 1] = "0123456789abcdef"[sum[i] & 15];
|
||||
}
|
||||
id[32] = 0;
|
||||
|
||||
cmb->cmb_boxid = strdup(id);
|
||||
|
||||
mailbox_tally++;
|
||||
|
||||
LIST_INSERT_HEAD(&mailboxes, cmb, cmb_link);
|
||||
|
||||
dtimer_arm(&cmb->cmb_timer, comet_mailbox_unused, cmb,
|
||||
MAILBOX_UNUSED_TIMEOUT);
|
||||
return cmb;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
comet_mailbox_reply(comet_mailbox_t *cmb, http_reply_t *hr)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
|
||||
htsmsg_add_str(m, "boxid", cmb->cmb_boxid);
|
||||
|
||||
htsmsg_add_msg(m, "messages", cmb->cmb_messages ?: htsmsg_create_array());
|
||||
cmb->cmb_messages = NULL;
|
||||
|
||||
htsmsg_json_serialize(m, &hr->hr_q, 0);
|
||||
|
||||
htsmsg_destroy(m);
|
||||
|
||||
http_output(hr->hr_connection, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
cmb->cmb_hr = NULL;
|
||||
|
||||
/* Arm a timer in case the browser won't call back */
|
||||
dtimer_arm(&cmb->cmb_timer, comet_mailbox_unused, cmb,
|
||||
MAILBOX_UNUSED_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
comet_mailbox_empty_reply(void *opaque, int64_t now)
|
||||
{
|
||||
comet_mailbox_t *cmb = opaque;
|
||||
http_reply_t *hr = cmb->cmb_hr;
|
||||
|
||||
comet_mailbox_reply(cmb, hr);
|
||||
http_continue(hr->hr_connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Poll callback
|
||||
*
|
||||
* Prepare the mailbox for reply
|
||||
*/
|
||||
static int
|
||||
comet_mailbox_poll(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
{
|
||||
comet_mailbox_t *cmb = NULL;
|
||||
const char *cometid = http_arg_get(&hc->hc_req_args, "boxid");
|
||||
|
||||
if(cometid != NULL)
|
||||
LIST_FOREACH(cmb, &mailboxes, cmb_link)
|
||||
if(!strcmp(cmb->cmb_boxid, cometid))
|
||||
break;
|
||||
|
||||
if(cmb == NULL)
|
||||
cmb = comet_mailbox_create();
|
||||
|
||||
if(cmb->cmb_hr != NULL) {
|
||||
mbdebug("mailbox already processing\n");
|
||||
return 409;
|
||||
}
|
||||
|
||||
if(cmb->cmb_messages != NULL) {
|
||||
/* Pending letters, direct reply */
|
||||
mbdebug("direct reply\n");
|
||||
comet_mailbox_reply(cmb, hr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mbdebug("nothing in queue, waiting\n");
|
||||
|
||||
cmb->cmb_hr = hr;
|
||||
|
||||
hr->hr_opaque = cmb;
|
||||
hr->hr_destroy = comet_mailbox_connection_lost;
|
||||
|
||||
dtimer_arm(&cmb->cmb_timer, comet_mailbox_empty_reply, cmb,
|
||||
MAILBOX_EMPTY_REPLY_TIMEOUT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
#if 0
|
||||
static dtimer_t comet_clock;
|
||||
|
||||
static void
|
||||
comet_ticktack(void *opaque, int64_t now)
|
||||
{
|
||||
char buf[64];
|
||||
htsmsg_t *x;
|
||||
|
||||
snprintf(buf, sizeof(buf), "The clock is now %lluµs past 1970",
|
||||
now);
|
||||
|
||||
x = htsmsg_create();
|
||||
htsmsg_add_str(x, "type", "logmessage");
|
||||
htsmsg_add_str(x, "logtxt", buf);
|
||||
|
||||
comet_mailbox_add_message(x);
|
||||
|
||||
htsmsg_destroy(x);
|
||||
dtimer_arm(&comet_clock, comet_ticktack, NULL, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
comet_init(void)
|
||||
{
|
||||
http_path_add("/comet", NULL, comet_mailbox_poll, ACCESS_WEB_INTERFACE);
|
||||
|
||||
// dtimer_arm(&comet_clock, comet_ticktack, NULL, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delayed delivery of mailbox replies
|
||||
*/
|
||||
static void
|
||||
comet_mailbox_deliver(void *opaque, int64_t now)
|
||||
{
|
||||
comet_mailbox_t *cmb = opaque;
|
||||
http_connection_t *hc;
|
||||
|
||||
hc = cmb->cmb_hr->hr_connection;
|
||||
comet_mailbox_reply(cmb, cmb->cmb_hr);
|
||||
http_continue(hc);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
comet_mailbox_add_message(htsmsg_t *m)
|
||||
{
|
||||
comet_mailbox_t *cmb;
|
||||
|
||||
LIST_FOREACH(cmb, &mailboxes, cmb_link) {
|
||||
|
||||
if(cmb->cmb_messages == NULL)
|
||||
cmb->cmb_messages = htsmsg_create_array();
|
||||
htsmsg_add_msg(cmb->cmb_messages, NULL, htsmsg_copy(m));
|
||||
|
||||
if(cmb->cmb_hr != NULL)
|
||||
dtimer_arm_hires(&cmb->cmb_timer, comet_mailbox_deliver, cmb,
|
||||
getclock_hires() + 100000);
|
||||
}
|
||||
}
|
176
webui/cwceditor.js
Normal file
176
webui/cwceditor.js
Normal file
|
@ -0,0 +1,176 @@
|
|||
|
||||
tvheadend.cwceditor = function() {
|
||||
var fm = Ext.form;
|
||||
|
||||
var enabledColumn = new Ext.grid.CheckColumn({
|
||||
header: "Enabled",
|
||||
dataIndex: 'enabled',
|
||||
width: 60
|
||||
});
|
||||
|
||||
var cm = new Ext.grid.ColumnModel([
|
||||
enabledColumn,
|
||||
{
|
||||
id:'hostname',
|
||||
header: "Hostname",
|
||||
dataIndex: 'hostname',
|
||||
width: 200,
|
||||
editor: new fm.TextField({
|
||||
allowBlank: false
|
||||
})
|
||||
},{
|
||||
id:'port',
|
||||
header: "Port",
|
||||
dataIndex: 'port',
|
||||
editor: new fm.TextField({
|
||||
allowBlank: false
|
||||
})
|
||||
},{
|
||||
id:'username',
|
||||
header: "Username",
|
||||
dataIndex: 'username',
|
||||
editor: new fm.TextField({
|
||||
allowBlank: false
|
||||
})
|
||||
},{
|
||||
id:'password',
|
||||
header: "Password",
|
||||
dataIndex: 'password',
|
||||
editor: new fm.TextField({
|
||||
allowBlank: false
|
||||
})
|
||||
},{
|
||||
header: "DES Key",
|
||||
dataIndex: 'deskey',
|
||||
width: 300,
|
||||
editor: new fm.TextField({
|
||||
allowBlank: false,
|
||||
})
|
||||
},
|
||||
{
|
||||
id:'comment',
|
||||
header: "Comment",
|
||||
dataIndex: 'comment',
|
||||
width: 400,
|
||||
editor: new fm.TextField({
|
||||
|
||||
})
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
cm.defaultSortable = true;
|
||||
|
||||
var UserRecord = Ext.data.Record.create([
|
||||
{name: 'enabled'},
|
||||
{name: 'hostname'},
|
||||
{name: 'port'},
|
||||
{name: 'username'},
|
||||
{name: 'password'},
|
||||
{name: 'deskey'},
|
||||
{name: 'comment'}]);
|
||||
|
||||
var store = new Ext.data.JsonStore({root: 'entries',
|
||||
fields: UserRecord,
|
||||
url: "tablemgr",
|
||||
autoLoad: true,
|
||||
id: 'id',
|
||||
storeid: 'id',
|
||||
baseParams: {table: "cwc", op: "get"}
|
||||
});
|
||||
|
||||
|
||||
function addRecord() {
|
||||
Ext.Ajax.request({
|
||||
url: "tablemgr", params: {op:"create", table:"cwc"},
|
||||
failure:function(response,options){
|
||||
Ext.MessageBox.alert('Server Error','Unable to generate new record');
|
||||
},
|
||||
success:function(response,options){
|
||||
var responseData = Ext.util.JSON.decode(response.responseText);
|
||||
|
||||
var p = new UserRecord(responseData, responseData.id);
|
||||
grid.stopEditing();
|
||||
store.insert(0, p);
|
||||
grid.startEditing(0, 0);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
|
||||
function delSelected() {
|
||||
var selectedKeys = grid.selModel.selections.keys;
|
||||
if(selectedKeys.length > 0)
|
||||
{
|
||||
Ext.MessageBox.confirm('Message',
|
||||
'Do you really want to delete selection?',
|
||||
deleteRecord);
|
||||
} else {
|
||||
Ext.MessageBox.alert('Message',
|
||||
'Please select at least one item to delete');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function deleteRecord(btn) {
|
||||
if(btn=='yes') {
|
||||
var selectedKeys = grid.selModel.selections.keys;
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: "tablemgr", params: {op:"delete", table:"cwc",
|
||||
entries:Ext.encode(selectedKeys)},
|
||||
failure:function(response,options){
|
||||
Ext.MessageBox.alert('Server Error','Unable to delete');
|
||||
},
|
||||
success:function(response,options){
|
||||
store.reload();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function saveChanges() {
|
||||
var mr = store.getModifiedRecords();
|
||||
var out = new Array();
|
||||
for (var x = 0; x < mr.length; x++) {
|
||||
v = mr[x].getChanges();
|
||||
out[x] = v;
|
||||
out[x].id = mr[x].id;
|
||||
}
|
||||
|
||||
Ext.Ajax.request({url: "tablemgr",
|
||||
params: {op:"update", table:"cwc",
|
||||
entries:Ext.encode(out)},
|
||||
success:function(response,options){
|
||||
store.commitChanges();
|
||||
},
|
||||
failure:function(response,options){
|
||||
Ext.MessageBox.alert('Message',response.statusText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var grid = new Ext.grid.EditorGridPanel({
|
||||
title: 'Code Word Client',
|
||||
plugins:[enabledColumn],
|
||||
store: store,
|
||||
clicksToEdit: 2,
|
||||
cm: cm,
|
||||
selModel: new Ext.grid.RowSelectionModel({singleSelect:false}),
|
||||
tbar: [{
|
||||
text: 'Add entry',
|
||||
handler: addRecord
|
||||
}, '-', {
|
||||
text: 'Delete selected',
|
||||
handler: delSelected
|
||||
}, '-', {
|
||||
text: "Save changes",
|
||||
handler: saveChanges
|
||||
}
|
||||
]
|
||||
});
|
||||
return grid;
|
||||
}
|
270
webui/dvb.js
Normal file
270
webui/dvb.js
Normal file
|
@ -0,0 +1,270 @@
|
|||
|
||||
|
||||
/**
|
||||
* DVB adapter details
|
||||
*/
|
||||
tvheadend.dvb_adapterdetails = function(adapterId, adapterName, treenode) {
|
||||
|
||||
var confreader = new Ext.data.JsonReader({
|
||||
root: 'dvbadapters',
|
||||
}, ['name', 'automux']);
|
||||
|
||||
var confpanel = new Ext.FormPanel({
|
||||
frame:true,
|
||||
disabled:true,
|
||||
region:'south',
|
||||
title:'Configuraton',
|
||||
split:true,
|
||||
height: 150,
|
||||
collapsible: true,
|
||||
margins:'0 0 0 0',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 150,
|
||||
waitMsgTarget: true,
|
||||
reader: confreader,
|
||||
defaultType: 'textfield',
|
||||
items: [{
|
||||
fieldLabel: 'Adapter name',
|
||||
name: 'name',
|
||||
width: 400,
|
||||
},
|
||||
|
||||
new Ext.form.Checkbox({
|
||||
fieldLabel: 'Autodetect muxes',
|
||||
name: 'automux',
|
||||
})
|
||||
],
|
||||
});
|
||||
|
||||
confpanel.getForm().load({url:'/dvbadapter',
|
||||
params:{'adapterId': adapterId, 'op':'load'},
|
||||
success:function(form, action) {
|
||||
confpanel.enable();
|
||||
}});
|
||||
|
||||
|
||||
|
||||
var submit = confpanel.addButton({
|
||||
text: 'Save changes',
|
||||
handler: function() {
|
||||
confpanel.getForm().submit({url:'/dvbadapter',
|
||||
params:{'adapterId': adapterId, 'op':'save'},
|
||||
waitMsg:'Saving Data...',
|
||||
success:function(form, action) {
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
var status = {
|
||||
region:'center',
|
||||
html: 'Status...',
|
||||
margins:'0 0 0 0'
|
||||
};
|
||||
|
||||
|
||||
function addmux() {
|
||||
|
||||
var locationbutton = new Ext.Button({
|
||||
text: 'Add',
|
||||
disabled: true,
|
||||
handler: function() {
|
||||
var n = locationlist.getSelectionModel().getSelectedNode();
|
||||
Ext.Ajax.request({url: '/dvbadapter',
|
||||
params: {network: n.attributes.id,
|
||||
adapterId: adapterId, op: 'addnetwork'}});
|
||||
win.close();
|
||||
}
|
||||
});
|
||||
|
||||
var locationlist = new Ext.tree.TreePanel({
|
||||
title:'By location',
|
||||
autoScroll:true,
|
||||
rootVisible:false,
|
||||
loader: new Ext.tree.TreeLoader({
|
||||
baseParams: {adapter: adapterId},
|
||||
dataUrl:'/dvbnetworks',
|
||||
}),
|
||||
|
||||
root: new Ext.tree.AsyncTreeNode({
|
||||
id:'root',
|
||||
}),
|
||||
|
||||
buttons: [locationbutton],
|
||||
buttonAlign: 'center'
|
||||
});
|
||||
|
||||
|
||||
locationlist.on('click', function(n) {
|
||||
if(n.attributes.leaf) {
|
||||
locationbutton.enable();
|
||||
} else {
|
||||
locationbutton.disable();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
var locationpanel = new Ext.FormPanel({
|
||||
frame:true,
|
||||
title:'By location',
|
||||
margins:'0 0 0 0',
|
||||
reader: confreader,
|
||||
defaultType: 'textfield',
|
||||
items: [locationlist]
|
||||
});
|
||||
var submit = locationpanel.addButton({
|
||||
text: 'Submit',
|
||||
handler: function() {
|
||||
alert('hej');
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
win = new Ext.Window({
|
||||
title: 'Add mux(es) on ' + adapterName,
|
||||
layout: 'fit',
|
||||
width: 500,
|
||||
height: 500,
|
||||
modal: true,
|
||||
plain: true,
|
||||
items: new Ext.TabPanel({
|
||||
autoTabs: true,
|
||||
activeTab: 0,
|
||||
deferredRender: false,
|
||||
border: false,
|
||||
items: [locationlist, {
|
||||
html: 'def',
|
||||
title: 'Manual configuration'
|
||||
}]
|
||||
})
|
||||
});
|
||||
win.show();
|
||||
}
|
||||
|
||||
|
||||
var panel = new Ext.Panel({
|
||||
title: adapterName,
|
||||
layout:'border',
|
||||
border: false,
|
||||
items:[status, confpanel],
|
||||
tbar:[{
|
||||
text: 'Add mux(es)',
|
||||
handler: addmux
|
||||
}]
|
||||
});
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
tvheadend.dvb = function() {
|
||||
|
||||
function refresh() {
|
||||
var sm = tree.getSelectionModel();
|
||||
console.log(sm.getSelectedNode());
|
||||
sm.getSelectedNode().reload();
|
||||
}
|
||||
|
||||
|
||||
function deleteNode() {
|
||||
var sm = tree.getSelectionModel();
|
||||
console.log(sm.getSelectedNode());
|
||||
}
|
||||
|
||||
var tree = new Ext.tree.ColumnTree({
|
||||
region:'west',
|
||||
autoScroll:true,
|
||||
rootVisible:false,
|
||||
|
||||
columns:[{
|
||||
header:'Name',
|
||||
width:300,
|
||||
dataIndex:'name'
|
||||
},{
|
||||
header:'Type',
|
||||
width:100,
|
||||
dataIndex:'type'
|
||||
},{
|
||||
header:'Status',
|
||||
width:100,
|
||||
dataIndex:'status'
|
||||
},{
|
||||
header:'Quality',
|
||||
width:100,
|
||||
dataIndex:'quality'
|
||||
}],
|
||||
|
||||
loader: new Ext.tree.TreeLoader({
|
||||
clearOnLoad: true,
|
||||
dataUrl:'/dvbtree',
|
||||
uiProviders:{
|
||||
'col': Ext.tree.ColumnNodeUI
|
||||
}
|
||||
}),
|
||||
|
||||
root: new Ext.tree.AsyncTreeNode({
|
||||
id:'root',
|
||||
}),
|
||||
|
||||
tbar: [{
|
||||
text: 'Delete selected',
|
||||
handler: deleteNode
|
||||
}, '-', {
|
||||
text: 'Refresh all',
|
||||
handler: refresh
|
||||
}],
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
var details = new Ext.Panel({
|
||||
region:'center', layout:'fit',
|
||||
items:[{border: false}]
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
var panel = new Ext.Panel({
|
||||
border: false,
|
||||
title:'DVB Adapters',
|
||||
layout:'border',
|
||||
items: [tree, details]
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
tree.on('click', function(n) {
|
||||
details.remove(details.getComponent(0));
|
||||
|
||||
details.doLayout();
|
||||
|
||||
switch(n.attributes.itype) {
|
||||
case 'adapter':
|
||||
var newPanel =
|
||||
new tvheadend.dvb_adapterdetails(n.attributes.id,
|
||||
n.attributes.name, n);
|
||||
break;
|
||||
|
||||
case 'mux':
|
||||
case 'transport':
|
||||
default:
|
||||
var newPanel = {title: n.attributes.name, html: ''};
|
||||
break;
|
||||
}
|
||||
|
||||
details.add(newPanel);
|
||||
details.doLayout();
|
||||
});
|
||||
|
||||
tvheadend.dvbtree = tree;
|
||||
|
||||
return panel;
|
||||
}
|
65
webui/ext.css
Normal file
65
webui/ext.css
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Ext JS Library 2.1
|
||||
* Copyright(c) 2006-2008, Ext JS, LLC.
|
||||
* licensing@extjs.com
|
||||
*
|
||||
* http://extjs.com/license
|
||||
*/
|
||||
|
||||
|
||||
.x-column-tree .x-tree-node {
|
||||
zoom:1;
|
||||
}
|
||||
.x-column-tree .x-tree-node-el {
|
||||
/*border-bottom:1px solid #eee; borders? */
|
||||
zoom:1;
|
||||
}
|
||||
.x-column-tree .x-tree-selected {
|
||||
background: #d9e8fb;
|
||||
}
|
||||
.x-column-tree .x-tree-node a {
|
||||
line-height:18px;
|
||||
vertical-align:middle;
|
||||
}
|
||||
.x-column-tree .x-tree-node a span{
|
||||
|
||||
}
|
||||
.x-column-tree .x-tree-node .x-tree-selected a span{
|
||||
background:transparent;
|
||||
color:#000;
|
||||
}
|
||||
.x-tree-col {
|
||||
float:left;
|
||||
overflow:hidden;
|
||||
padding:0 1px;
|
||||
zoom:1;
|
||||
}
|
||||
|
||||
.x-tree-col-text, .x-tree-hd-text {
|
||||
overflow:hidden;
|
||||
-o-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
padding:3px 3px 3px 5px;
|
||||
white-space: nowrap;
|
||||
font:normal 11px arial, tahoma, helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.x-tree-headers {
|
||||
background: #f9f9f9 url(http://192.168.1.50/lab/extjs/resources/images/default/grid/grid3-hrow.gif) repeat-x 0 bottom;
|
||||
cursor:default;
|
||||
zoom:1;
|
||||
}
|
||||
|
||||
.x-tree-hd {
|
||||
float:left;
|
||||
overflow:hidden;
|
||||
border-left:1px solid #eee;
|
||||
border-right:1px solid #d0d0d0;
|
||||
}
|
||||
/*
|
||||
.task {
|
||||
background-image:url(../shared/icons/fam/cog.png) !important;
|
||||
}
|
||||
.task-folder {
|
||||
background-image:url(../shared/icons/fam/folder_go.png) !important;
|
||||
}*/
|
145
webui/extensions.js
Normal file
145
webui/extensions.js
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Ext JS Library 2.1
|
||||
* Copyright(c) 2006-2008, Ext JS, LLC.
|
||||
* licensing@extjs.com
|
||||
*
|
||||
* http://extjs.com/license
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* CheckedColumn
|
||||
*/
|
||||
|
||||
Ext.grid.CheckColumn = function(config){
|
||||
Ext.apply(this, config);
|
||||
if(!this.id){
|
||||
this.id = Ext.id();
|
||||
}
|
||||
this.renderer = this.renderer.createDelegate(this);
|
||||
};
|
||||
|
||||
Ext.grid.CheckColumn.prototype ={
|
||||
init : function(grid){
|
||||
this.grid = grid;
|
||||
this.grid.on('render', function(){
|
||||
var view = this.grid.getView();
|
||||
view.mainBody.on('mousedown', this.onMouseDown, this);
|
||||
}, this);
|
||||
},
|
||||
|
||||
onMouseDown : function(e, t){
|
||||
if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){
|
||||
e.stopEvent();
|
||||
var index = this.grid.getView().findRowIndex(t);
|
||||
var record = this.grid.store.getAt(index);
|
||||
record.set(this.dataIndex, !record.data[this.dataIndex]);
|
||||
}
|
||||
},
|
||||
|
||||
renderer : function(v, p, record){
|
||||
p.css += ' x-grid3-check-col-td';
|
||||
return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'"> </div>';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* ColumnTree
|
||||
*/
|
||||
|
||||
|
||||
Ext.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, {
|
||||
lines:false,
|
||||
borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
|
||||
cls:'x-column-tree',
|
||||
|
||||
onRender : function(){
|
||||
Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments);
|
||||
this.headers = this.body.createChild(
|
||||
{cls:'x-tree-headers'},this.innerCt.dom);
|
||||
|
||||
var cols = this.columns, c;
|
||||
var totalWidth = 0;
|
||||
|
||||
for(var i = 0, len = cols.length; i < len; i++){
|
||||
c = cols[i];
|
||||
totalWidth += c.width;
|
||||
this.headers.createChild({
|
||||
cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
|
||||
cn: {
|
||||
cls:'x-tree-hd-text',
|
||||
html: c.header
|
||||
},
|
||||
style:'width:'+(c.width-this.borderWidth)+'px;'
|
||||
});
|
||||
}
|
||||
this.headers.createChild({cls:'x-clear'});
|
||||
// prevent floats from wrapping when clipped
|
||||
this.headers.setWidth(totalWidth);
|
||||
this.innerCt.setWidth(totalWidth);
|
||||
}
|
||||
});
|
||||
|
||||
Ext.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
|
||||
|
||||
focus: Ext.emptyFn, // prevent odd scrolling behavior
|
||||
|
||||
setColText : function(colidx, text) {
|
||||
this.colNode[colidx].innerHTML = text;
|
||||
},
|
||||
|
||||
renderElements : function(n, a, targetNode, bulkRender){
|
||||
this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
|
||||
this.colNode = [];
|
||||
|
||||
|
||||
var t = n.getOwnerTree();
|
||||
var cols = t.columns;
|
||||
var bw = t.borderWidth;
|
||||
var c = cols[0];
|
||||
|
||||
var buf = [
|
||||
'<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf ', a.cls,'">',
|
||||
'<div class="x-tree-col" style="width:',c.width-bw,'px;">',
|
||||
'<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
|
||||
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">',
|
||||
'<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on">',
|
||||
'<a hidefocus="on" class="x-tree-node-anchor" href="',a.href ? a.href : "#",'" tabIndex="1" ',
|
||||
a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>',
|
||||
'<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</span></a>",
|
||||
"</div>"];
|
||||
for(var i = 1, len = cols.length; i < len; i++){
|
||||
c = cols[i];
|
||||
|
||||
buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
|
||||
'<div class="x-tree-col-text">',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</div>",
|
||||
"</div>");
|
||||
}
|
||||
buf.push(
|
||||
'<div class="x-clear"></div></div>',
|
||||
'<ul class="x-tree-node-ct" style="display:none;"></ul>',
|
||||
"</li>");
|
||||
|
||||
if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
|
||||
this.wrap = Ext.DomHelper.insertHtml("beforeBegin",
|
||||
n.nextSibling.ui.getEl(), buf.join(""));
|
||||
}else{
|
||||
this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
|
||||
}
|
||||
|
||||
this.elNode = this.wrap.childNodes[0];
|
||||
this.ctNode = this.wrap.childNodes[1];
|
||||
var cs = this.elNode.firstChild.childNodes;
|
||||
this.indentNode = cs[0];
|
||||
this.ecNode = cs[1];
|
||||
this.iconNode = cs[2];
|
||||
this.anchor = cs[3];
|
||||
this.textNode = cs[3].firstChild;
|
||||
|
||||
for(var i = 1, len = cols.length; i < len; i++) {
|
||||
this.colNode[i] = this.elNode.childNodes[i].firstChild;
|
||||
}
|
||||
}
|
||||
});
|
416
webui/extjs.c
Normal file
416
webui/extjs.c
Normal file
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
* tvheadend, EXTJS based 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 <stdarg.h>
|
||||
|
||||
#include <libhts/htsmsg.h>
|
||||
#include <libhts/htsmsg_json.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "http.h"
|
||||
#include "webui.h"
|
||||
#include "access.h"
|
||||
#include "dtable.h"
|
||||
|
||||
#include "dvb.h"
|
||||
#include "dvb_support.h"
|
||||
#include "dvb_muxconfig.h"
|
||||
#include "transports.h"
|
||||
|
||||
extern const char *htsversion;
|
||||
|
||||
#include "obj/tvheadend.jsh"
|
||||
#include "obj/extensions.jsh"
|
||||
#include "obj/acleditor.jsh"
|
||||
#include "obj/cwceditor.jsh"
|
||||
#include "obj/dvb.jsh"
|
||||
#include "obj/ext.cssh"
|
||||
|
||||
|
||||
static void
|
||||
extjs_load(htsbuf_queue_t *hq, const char *script)
|
||||
{
|
||||
htsbuf_qprintf(hq,
|
||||
"<script type=\"text/javascript\" "
|
||||
"src=\"%s\">"
|
||||
"</script>\n", script);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
extjs_exec(htsbuf_queue_t *hq, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
htsbuf_qprintf(hq, "<script type=\"text/javascript\">\r\n");
|
||||
|
||||
va_start(ap, fmt);
|
||||
htsbuf_vqprintf(hq, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
htsbuf_qprintf(hq, "\r\n</script>\r\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PVR info, deliver info about the given PVR entry
|
||||
*/
|
||||
static int
|
||||
extjs_root(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
|
||||
#define EXTJSPATH "http://www.lonelycoder.com/ext-2.2"
|
||||
|
||||
htsbuf_qprintf(hq, "<html><body>\n"
|
||||
"<link rel=\"stylesheet\" type=\"text/css\" href=\""EXTJSPATH"/resources/css/ext-all.css\">\n"
|
||||
"<link rel=\"stylesheet\" type=\"text/css\" href=\"app/extensions.css\">\n"
|
||||
"<script type=\"text/javascript\" src=\""EXTJSPATH"/adapter/ext/ext-base.js\"></script>\n"
|
||||
"<script type=\"text/javascript\" src=\""EXTJSPATH"/ext-all-debug.js\"></script>\n");
|
||||
|
||||
extjs_exec(hq, "Ext.BLANK_IMAGE_URL = "
|
||||
"'"EXTJSPATH"/resources/images/default/s.gif';");
|
||||
|
||||
/**
|
||||
* Load extjs extensions
|
||||
*/
|
||||
extjs_load(hq, "app/extensions.js");
|
||||
extjs_load(hq, "app/tabclosemenu.js");
|
||||
|
||||
/**
|
||||
* Create a namespace for our app
|
||||
*/
|
||||
extjs_exec(hq, "Ext.namespace('tvheadend');");
|
||||
|
||||
/**
|
||||
* Load all components
|
||||
*/
|
||||
extjs_load(hq, "app/acleditor.js");
|
||||
extjs_load(hq, "app/cwceditor.js");
|
||||
extjs_load(hq, "app/dvb.js");
|
||||
|
||||
/**
|
||||
* Finally, the app itself
|
||||
*/
|
||||
extjs_load(hq, "app/tvheadend.js");
|
||||
extjs_exec(hq, "Ext.onReady(tvheadend.app.init, tvheadend.app);");
|
||||
|
||||
|
||||
|
||||
|
||||
htsbuf_qprintf(hq,
|
||||
"<style type=\"text/css\">\n"
|
||||
"html, body {\n"
|
||||
"\tfont:normal 12px verdana;\n"
|
||||
"\tmargin:0;\n"
|
||||
"\tpadding:0;\n"
|
||||
"\tborder:0 none;\n"
|
||||
"\toverflow:hidden;\n"
|
||||
"\theight:100%;\n"
|
||||
"}\n"
|
||||
"#systemlog {\n"
|
||||
"\tfont:normal 12px courier; font-weight: bold;\n"
|
||||
"}\n"
|
||||
"p {\n"
|
||||
"\tmargin:5px;\n"
|
||||
"}\n"
|
||||
"</style>\n"
|
||||
"<title>HTS Tvheadend %s</title>\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
"<div id=\"systemlog\"></div>\n"
|
||||
"</body></html>\n",
|
||||
htsversion);
|
||||
http_output_html(hc, hr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
dtable_t *dt;
|
||||
htsmsg_t *out = NULL, *in, *array;
|
||||
|
||||
const char *tablename = http_arg_get(&hc->hc_req_args, "table");
|
||||
const char *op = http_arg_get(&hc->hc_req_args, "op");
|
||||
const char *entries = http_arg_get(&hc->hc_req_args, "entries");
|
||||
|
||||
if(tablename == NULL || (dt = dtable_find(tablename)) == NULL)
|
||||
return 404;
|
||||
|
||||
in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
|
||||
|
||||
if(!strcmp(op, "create")) {
|
||||
out = dtable_record_create(dt);
|
||||
|
||||
} else if(!strcmp(op, "get")) {
|
||||
array = dtable_record_get_all(dt);
|
||||
|
||||
out = htsmsg_create();
|
||||
htsmsg_add_msg(out, "entries", array);
|
||||
|
||||
} else if(!strcmp(op, "update")) {
|
||||
if(in == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
dtable_record_update_by_array(dt, in);
|
||||
|
||||
} else if(!strcmp(op, "delete")) {
|
||||
if(in == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
dtable_record_delete_by_array(dt, in);
|
||||
|
||||
} else {
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if(in != NULL)
|
||||
htsmsg_destroy(in);
|
||||
|
||||
if(out != NULL) {
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
}
|
||||
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
extjs_dvbtree_node(htsmsg_t *array, int leaf, const char *id, const char *name,
|
||||
const char *type, const char *status, int quality,
|
||||
const char *itype)
|
||||
{
|
||||
htsmsg_t *e = htsmsg_create();
|
||||
|
||||
htsmsg_add_str(e, "uiProvider", "col");
|
||||
htsmsg_add_str(e, "id", id);
|
||||
htsmsg_add_u32(e, "leaf", leaf);
|
||||
htsmsg_add_str(e, "itype", itype);
|
||||
|
||||
htsmsg_add_str(e, "name", name);
|
||||
htsmsg_add_str(e, "type", type);
|
||||
htsmsg_add_str(e, "status", status);
|
||||
htsmsg_add_u32(e, "quality", quality);
|
||||
|
||||
htsmsg_add_msg(array, NULL, e);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
const char *s = http_arg_get(&hc->hc_req_args, "node");
|
||||
htsmsg_t *out = NULL;
|
||||
char buf[200];
|
||||
th_dvb_adapter_t *tda;
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
th_transport_t *t;
|
||||
|
||||
if(s == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
out = htsmsg_create_array();
|
||||
|
||||
if(!strcmp(s, "root")) {
|
||||
/**
|
||||
* List of all adapters
|
||||
*/
|
||||
|
||||
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s adapter",
|
||||
dvb_adaptertype_to_str(tda->tda_type));
|
||||
|
||||
extjs_dvbtree_node(out, 0,
|
||||
tda->tda_identifier, tda->tda_displayname,
|
||||
buf, tda->tda_rootpath != NULL ? "OK" : "No H/W",
|
||||
100, "adapter");
|
||||
}
|
||||
} else if((tda = dvb_adapter_find_by_identifier(s)) != NULL) {
|
||||
|
||||
RB_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
|
||||
dvb_mux_nicename(buf, sizeof(buf), tdmi);
|
||||
|
||||
extjs_dvbtree_node(out, 0,
|
||||
tdmi->tdmi_identifier, buf, "DVB Mux",
|
||||
dvb_mux_status(tdmi, 0),
|
||||
100 + tdmi->tdmi_quality * 2, "mux");
|
||||
}
|
||||
} else if((tdmi = dvb_mux_find_by_identifier(s)) != NULL) {
|
||||
|
||||
LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) {
|
||||
|
||||
if(transport_servicetype_txt(t) == NULL)
|
||||
continue;
|
||||
|
||||
extjs_dvbtree_node(out, 1,
|
||||
t->tht_identifier, t->tht_svcname,
|
||||
transport_servicetype_txt(t),
|
||||
"OK", 100, "transport");
|
||||
}
|
||||
}
|
||||
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
extjs_dvbnetworks(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
const char *s = http_arg_get(&hc->hc_req_args, "node");
|
||||
const char *a = http_arg_get(&hc->hc_req_args, "adapter");
|
||||
th_dvb_adapter_t *tda;
|
||||
htsmsg_t *out = NULL;
|
||||
|
||||
if(s == NULL || a == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
if((tda = dvb_adapter_find_by_identifier(a)) == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
out = dvb_mux_preconf_get_node(tda->tda_type, s);
|
||||
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
json_single_record(htsmsg_t *rec, const char *root)
|
||||
{
|
||||
htsmsg_t *out, *array;
|
||||
|
||||
out = htsmsg_create();
|
||||
array = htsmsg_create_array();
|
||||
|
||||
htsmsg_add_msg(array, NULL, rec);
|
||||
htsmsg_add_msg(out, "dvbadapters", array);
|
||||
return out;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
extjs_dvbadapter(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hr->hr_q;
|
||||
const char *s = http_arg_get(&hc->hc_req_args, "adapterId");
|
||||
const char *op = http_arg_get(&hc->hc_req_args, "op");
|
||||
th_dvb_adapter_t *tda = s ? dvb_adapter_find_by_identifier(s) : NULL;
|
||||
|
||||
htsmsg_t *r, *out;
|
||||
|
||||
if(tda == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
if(!strcmp(op, "load")) {
|
||||
r = htsmsg_create();
|
||||
htsmsg_add_str(r, "id", tda->tda_identifier);
|
||||
htsmsg_add_str(r, "device", tda->tda_rootpath ?: "No hardware attached");
|
||||
htsmsg_add_str(r, "name", tda->tda_displayname);
|
||||
htsmsg_add_u32(r, "automux", 1);
|
||||
|
||||
out = json_single_record(r, "dvbadapters");
|
||||
} else if(!strcmp(op, "save")) {
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "name")) != NULL)
|
||||
dvb_tda_set_displayname(tda, s);
|
||||
|
||||
out = htsmsg_create();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
} else if(!strcmp(op, "addnetwork")) {
|
||||
if((s = http_arg_get(&hc->hc_req_args, "network")) != NULL)
|
||||
dvb_mux_preconf_add_network(tda, s);
|
||||
|
||||
out = htsmsg_create();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} else {
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
|
||||
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* WEB user interface
|
||||
*/
|
||||
void
|
||||
extjs_start(void)
|
||||
{
|
||||
http_path_add("/extjs.html", NULL, extjs_root, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/tablemgr", NULL, extjs_tablemgr, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/dvbtree", NULL, extjs_dvbtree, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/dvbadapter", NULL, extjs_dvbadapter, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/dvbnetworks", NULL, extjs_dvbnetworks, ACCESS_WEB_INTERFACE);
|
||||
|
||||
#define ADD_JS_RESOURCE(path, name) \
|
||||
http_resource_add(path, name, sizeof(name), "text/javascript", "gzip")
|
||||
|
||||
ADD_JS_RESOURCE("/app/extensions.js", embedded_extensions);
|
||||
ADD_JS_RESOURCE("/app/extensions.css", embedded_ext);
|
||||
|
||||
ADD_JS_RESOURCE("/app/tvheadend.js", embedded_tvheadend);
|
||||
|
||||
ADD_JS_RESOURCE("/app/acleditor.js", embedded_acleditor);
|
||||
ADD_JS_RESOURCE("/app/cwceditor.js", embedded_cwceditor);
|
||||
ADD_JS_RESOURCE("/app/dvb.js", embedded_dvb);
|
||||
}
|
135
webui/tvheadend.js
Normal file
135
webui/tvheadend.js
Normal file
|
@ -0,0 +1,135 @@
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* Comet interfaces
|
||||
*/
|
||||
tvheadend.comet_poller = function() {
|
||||
|
||||
function parse_comet_response(responsetxt) {
|
||||
|
||||
var response = Ext.util.JSON.decode(responsetxt);
|
||||
|
||||
for (var x = 0; x < response.messages.length; x++) {
|
||||
var m = response.messages[x];
|
||||
|
||||
switch(m.notificationClass) {
|
||||
case 'logmessage':
|
||||
|
||||
var sl = Ext.get('systemlog');
|
||||
var e = Ext.DomHelper.append(sl,
|
||||
'<div>' + m.logtxt + '</div>');
|
||||
e.scrollIntoView(sl);
|
||||
break;
|
||||
|
||||
case 'dvbadapter':
|
||||
case 'dvbmux':
|
||||
case 'dvbtransport':
|
||||
var n = tvheadend.dvbtree.getNodeById(m.id);
|
||||
if(n != null) {
|
||||
|
||||
|
||||
if(m.reload != null && n.isLoaded()) {
|
||||
n.reload();
|
||||
}
|
||||
|
||||
if(m.name != null) {
|
||||
n.setText(m.name);
|
||||
n.attributes.name = m.name;
|
||||
}
|
||||
|
||||
if(m.quality != null) {
|
||||
n.getUI().setColText(3, m.quality);
|
||||
n.attributes.quality = m.quality;
|
||||
}
|
||||
|
||||
if(m.status != null) {
|
||||
n.getUI().setColText(2, m.status);
|
||||
n.attributes.status = m.status;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: '/comet',
|
||||
params : { boxid: response.boxid },
|
||||
success: function(result, request) {
|
||||
parse_comet_response(result.responseText);
|
||||
}});
|
||||
|
||||
};
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: '/comet',
|
||||
success: function(result, request) {
|
||||
parse_comet_response(result.responseText);
|
||||
}});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
// create application
|
||||
tvheadend.app = function() {
|
||||
|
||||
// public space
|
||||
return {
|
||||
|
||||
// public methods
|
||||
init: function() {
|
||||
|
||||
var confpanel = new Ext.TabPanel({
|
||||
activeTab:0,
|
||||
autoScroll:true,
|
||||
title: 'Configuration',
|
||||
items: [new tvheadend.dvb,
|
||||
new tvheadend.acleditor,
|
||||
new tvheadend.cwceditor]
|
||||
});
|
||||
|
||||
var pvrpanel = new Ext.TabPanel({
|
||||
autoScroll:true,
|
||||
title: 'Video Recorder'
|
||||
});
|
||||
|
||||
var chpanel = new Ext.TabPanel({
|
||||
autoScroll:true,
|
||||
title: 'Channels'
|
||||
});
|
||||
|
||||
|
||||
var viewport = new Ext.Viewport({
|
||||
layout:'border',
|
||||
items:[
|
||||
{
|
||||
region:'south',
|
||||
contentEl: 'systemlog',
|
||||
split:true,
|
||||
autoScroll:true,
|
||||
height: 150,
|
||||
minSize: 100,
|
||||
maxSize: 400,
|
||||
collapsible: true,
|
||||
title:'System log',
|
||||
margins:'0 0 0 0'
|
||||
},
|
||||
new Ext.TabPanel({region:'center',
|
||||
activeTab:0,
|
||||
items:[confpanel,
|
||||
pvrpanel,
|
||||
chpanel]})
|
||||
]
|
||||
});
|
||||
|
||||
new tvheadend.comet_poller;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
}(); // end of app
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
#include "pvr.h"
|
||||
|
||||
#define ACCESS_SIMPLE \
|
||||
(ACCESS_WEB_INTERFACE | ACCESS_RECORDER_VIEW | ACCESS_RECORDER_CHANGE)
|
||||
(ACCESS_WEB_INTERFACE | ACCESS_RECORDER)
|
||||
|
||||
static struct strtab recstatustxt[] = {
|
||||
{ "Recording scheduled", HTSTV_PVR_STATUS_SCHEDULED },
|
||||
|
@ -108,7 +108,7 @@ page_root(http_connection_t *hc, http_reply_t *hr,
|
|||
if(is_client_simple(hc)) {
|
||||
http_redirect(hc, hr, "/simple.html");
|
||||
} else {
|
||||
http_redirect(hc, hr, "/ajax/index.html");
|
||||
http_redirect(hc, hr, "/extjs.html");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -418,7 +418,6 @@ page_pvrinfo(http_connection_t *hc, http_reply_t *hr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WEB user interface
|
||||
*/
|
||||
|
@ -430,5 +429,8 @@ webui_start(void)
|
|||
http_path_add("/simple.html", NULL, page_simple, ACCESS_SIMPLE);
|
||||
http_path_add("/eventinfo", NULL, page_einfo, ACCESS_SIMPLE);
|
||||
http_path_add("/pvrinfo", NULL, page_pvrinfo, ACCESS_SIMPLE);
|
||||
|
||||
|
||||
extjs_start();
|
||||
|
||||
comet_init();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,15 @@
|
|||
#ifndef WEBUI_H_
|
||||
#define WEBUI_H_
|
||||
|
||||
#include <libhts/htsmsg.h>
|
||||
|
||||
void webui_start(void);
|
||||
|
||||
void extjs_start(void);
|
||||
|
||||
void comet_init(void);
|
||||
|
||||
void comet_mailbox_add_message(htsmsg_t *m);
|
||||
|
||||
|
||||
#endif /* WEBUI_H_ */
|
||||
|
|
2
xbmsp.c
2
xbmsp.c
|
@ -422,7 +422,7 @@ xbmsp_send_err(xbmsp_t *xbmsp, uint32_t msgid, uint8_t errcode,
|
|||
vsnprintf(errbuf, sizeof(errbuf), errfmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
syslog(LOG_INFO, "%s: %s", xbmsp->xbmsp_logname, errbuf);
|
||||
tvhlog(LOG_INFO, "xbmsp", "%s: %s", xbmsp->xbmsp_logname, errbuf);
|
||||
|
||||
slen = strlen(errbuf);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue