diff --git a/Makefile b/Makefile index 8df370f0..7558fefd 100644 --- a/Makefile +++ b/Makefile @@ -191,6 +191,7 @@ SRCS-${CONFIG_LINUXDVB} += \ src/input/mpegts/linuxdvb/linuxdvb_lnb.c \ src/input/mpegts/linuxdvb/linuxdvb_switch.c \ src/input/mpegts/linuxdvb/linuxdvb_rotor.c \ + src/input/mpegts/linuxdvb/scanfile.c \ # IPTV SRCS-${CONFIG_IPTV} += \ diff --git a/src/api/api_mpegts.c b/src/api/api_mpegts.c index b75fe6e7..3bb8227c 100644 --- a/src/api/api_mpegts.c +++ b/src/api/api_mpegts.c @@ -25,6 +25,7 @@ #if ENABLE_LINUXDVB #include "input/mpegts/linuxdvb.h" #include "input/mpegts/linuxdvb/linuxdvb_private.h" +#include "input/mpegts/linuxdvb/scanfile.h" #endif /* @@ -271,6 +272,52 @@ api_linuxdvb_satconf_create return err; } + +static int +api_linuxdvb_scanfile_list + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + char buf[512]; + const char *type = htsmsg_get_str(args, "type"); + scanfile_region_list_t *list = NULL; + htsmsg_t *l, *e; + scanfile_region_t *r; + scanfile_network_t *n; + + if (!type) + return -EINVAL; + + if (!strcasecmp(type, "dvbt")) + list = &scanfile_regions_DVBT; + else if (!strcasecmp(type, "dvbc")) + list = &scanfile_regions_DVBC; + else if (!strcasecmp(type, "dvbs")) + list = &scanfile_regions_DVBS; + else if (!strcasecmp(type, "atsc")) + list = &scanfile_regions_ATSC; + else + return -EINVAL; + + l = htsmsg_create_list(); + LIST_FOREACH(r, list, sfr_link) { + LIST_FOREACH(n, &r->sfr_networks, sfn_link) { + e = htsmsg_create_map(); + sprintf(buf, "%s/%s/%s", type, r->sfr_id, n->sfn_id); + htsmsg_add_str(e, "key", buf); + if (list != &scanfile_regions_DVBS) { + sprintf(buf, "%s: %s", r->sfr_name, n->sfn_name); + htsmsg_add_str(e, "val", buf); + } else { + htsmsg_add_str(e, "val", n->sfn_name); + } + htsmsg_add_msg(l, NULL, e); + } + } + *resp = htsmsg_create_map(); + htsmsg_add_msg(*resp, "entries", l); + + return 0; +} #endif /* @@ -316,6 +363,7 @@ api_mpegts_init ( void ) { "linuxdvb/satconf/grid", ACCESS_ANONYMOUS, api_idnode_grid, api_linuxdvb_satconf_grid }, { "linuxdvb/satconf/class", ACCESS_ANONYMOUS, api_idnode_class, (void*)&linuxdvb_satconf_class }, { "linuxdvb/satconf/create", ACCESS_ANONYMOUS, api_linuxdvb_satconf_create, NULL }, + { "linuxdvb/scanfile/list", ACCESS_ANONYMOUS, api_linuxdvb_scanfile_list, NULL }, #endif { NULL }, }; diff --git a/src/api/api_subscriptions.c b/src/api/api_subscriptions.c new file mode 100644 index 00000000..1d0c4b1a --- /dev/null +++ b/src/api/api_subscriptions.c @@ -0,0 +1,63 @@ +/* + * API - service related calls + * + * Copyright (C) 2013 Adam Sutton + * + * 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 . + */ + +#ifndef __TVH_API_SERVICE_H__ +#define __TVH_API_SERVICE_H__ + +#include "tvheadend.h" +#include "subscriptions.h" +#include "access.h" +#include "api.h" + +static int +api_subscription_list + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int c; + htsmsg_t *l, *e; + th_subscription_t *ths; + + l = htsmsg_create_list(); + c = 0; + LIST_FOREACH(ths, &subscriptions, ths_global_link) { + e = subscription_create_msg(ths); + htsmsg_add_msg(l, NULL, e); + c++; + } + + *resp = htsmsg_create_map(); + htsmsg_add_msg(*resp, "entries", l); + htsmsg_add_msg(*resp, "totalCount", c); + + return 0; +} + +void api_service_init ( void ) +{ + extern const idclass_t service_class; + static api_hook_t ah[] = { + { "subscription/list", ACCESS_ANONYMOUS, api_subscribtion_list, NULL }, + { NULL }, + }; + + api_register_all(ah); +} + + +#endif /* __TVH_API_IDNODE_H__ */ diff --git a/src/input/mpegts/dvb.h b/src/input/mpegts/dvb.h index 721f1b62..00d2cdf5 100644 --- a/src/input/mpegts/dvb.h +++ b/src/input/mpegts/dvb.h @@ -206,7 +206,7 @@ typedef enum polarisation { typedef struct dvb_mux_conf { dvb_frontend_parameters_t dmc_fe_params; - + // Additional DVB-S fields polarisation_t dmc_fe_polarisation; int dmc_fe_orbital_pos; @@ -216,6 +216,10 @@ typedef struct dvb_mux_conf fe_delivery_system_t dmc_fe_delsys; fe_rolloff_t dmc_fe_rolloff; #endif + + // For scan file configurations + LIST_ENTRY(dvb_mux_conf) dmc_link; + } dvb_mux_conf_t; const char *dvb_mux_conf_load diff --git a/src/input/mpegts/dvb_support.c b/src/input/mpegts/dvb_support.c index 203956f3..3e03b5c5 100644 --- a/src/input/mpegts/dvb_support.c +++ b/src/input/mpegts/dvb_support.c @@ -499,10 +499,10 @@ const static struct strtab hiertab[] = { dvb_str2val(hier); const static struct strtab poltab[] = { - { "Vertical", POLARISATION_VERTICAL }, - { "Horizontal", POLARISATION_HORIZONTAL }, - { "Left", POLARISATION_CIRCULAR_LEFT }, - { "Right", POLARISATION_CIRCULAR_RIGHT }, + { "V", POLARISATION_VERTICAL }, + { "H", POLARISATION_HORIZONTAL }, + { "L", POLARISATION_CIRCULAR_LEFT }, + { "R", POLARISATION_CIRCULAR_RIGHT }, }; dvb_str2val(pol); diff --git a/src/input/mpegts/linuxdvb/linuxdvb.c b/src/input/mpegts/linuxdvb/linuxdvb.c index c283326a..fca78181 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb.c +++ b/src/input/mpegts/linuxdvb/linuxdvb.c @@ -21,9 +21,13 @@ #include "input.h" #include "settings.h" #include "linuxdvb_private.h" +#include "scanfile.h" void linuxdvb_init ( int adapter_mask ) { + /* Load scan files */ + scanfile_init(); + /* Initialise networks */ linuxdvb_network_init(); diff --git a/src/input/mpegts/linuxdvb/linuxdvb_network.c b/src/input/mpegts/linuxdvb/linuxdvb_network.c index d02cb8db..2c9d0757 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_network.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_network.c @@ -22,6 +22,7 @@ #include "linuxdvb_private.h" #include "queue.h" #include "settings.h" +#include "scanfile.h" #include #include @@ -30,6 +31,10 @@ #include #include +static mpegts_mux_t * +linuxdvb_network_find_mux + ( linuxdvb_network_t *ln, dvb_mux_conf_t *dmc ); + /* **************************************************************************** * Class definition * ***************************************************************************/ @@ -49,6 +54,70 @@ linuxdvb_network_class_delete ( idnode_t *in ) mpegts_network_delete(mn); } +static const void * +linuxdvb_network_class_scanfile_get ( void *o ) +{ + static const char *s = NULL; + return &s; +} +static int +linuxdvb_network_class_scanfile_set ( void *o, const void *s ) +{ + dvb_mux_conf_t *dmc; + scanfile_network_t *sfn; + mpegts_mux_t *mm; + + /* Find */ + if (!s) + return 0; + if (!(sfn = scanfile_find(s))) + return 0; + + /* Create */ + LIST_FOREACH(dmc, &sfn->sfn_muxes, dmc_link) { + if (!(mm = linuxdvb_network_find_mux(o, dmc))) { + mm = (mpegts_mux_t*)linuxdvb_mux_create0(o, + MPEGTS_ONID_NONE, + MPEGTS_TSID_NONE, + dmc, NULL, NULL); + if (mm) + mm->mm_config_save(mm); + } + } + return 0; +} +static htsmsg_t * +linuxdvb_network_class_scanfile_list ( const char *type ) +{ + htsmsg_t *e, *m = htsmsg_create_map(); + htsmsg_add_str(m, "type", "api"); + htsmsg_add_str(m, "uri", "linuxdvb/scanfile/list"); + e = htsmsg_create_map(); + htsmsg_add_str(e, "type", type); + htsmsg_add_msg(m, "params", e); + return m; +} +static htsmsg_t * +linuxdvb_network_dvbt_class_scanfile_list ( void *o ) +{ + return linuxdvb_network_class_scanfile_list("dvbt"); +} +static htsmsg_t * +linuxdvb_network_dvbc_class_scanfile_list ( void *o ) +{ + return linuxdvb_network_class_scanfile_list("dvbc"); +} +static htsmsg_t * +linuxdvb_network_dvbs_class_scanfile_list ( void *o ) +{ + return linuxdvb_network_class_scanfile_list("dvbs"); +} +static htsmsg_t * +linuxdvb_network_atsc_class_scanfile_list ( void *o ) +{ + return linuxdvb_network_class_scanfile_list("atsc"); +} + const idclass_t linuxdvb_network_class = { .ic_super = &mpegts_network_class, @@ -66,6 +135,15 @@ const idclass_t linuxdvb_network_dvbt_class = .ic_class = "linuxdvb_network_dvbt", .ic_caption = "DVB-T Network", .ic_properties = (const property_t[]) { + { + .type = PT_STR, + .id = "scanfile", + .name = "Pre-defined Muxes", + .set = linuxdvb_network_class_scanfile_set, + .get = linuxdvb_network_class_scanfile_get, + .list = linuxdvb_network_dvbt_class_scanfile_list, + .opts = PO_NOSAVE, + }, {} } }; @@ -76,6 +154,15 @@ const idclass_t linuxdvb_network_dvbc_class = .ic_class = "linuxdvb_network_dvbc", .ic_caption = "DVB-C Network", .ic_properties = (const property_t[]) { + { + .type = PT_STR, + .id = "scanfile", + .name = "Pre-defined Muxes", + .set = linuxdvb_network_class_scanfile_set, + .get = linuxdvb_network_class_scanfile_get, + .list = linuxdvb_network_dvbc_class_scanfile_list, + .opts = PO_NOSAVE, + }, {} } }; @@ -86,6 +173,15 @@ const idclass_t linuxdvb_network_dvbs_class = .ic_class = "linuxdvb_network_dvbs", .ic_caption = "DVB-S Network", .ic_properties = (const property_t[]) { + { + .type = PT_STR, + .id = "scanfile", + .name = "Pre-defined Muxes", + .set = linuxdvb_network_class_scanfile_set, + .get = linuxdvb_network_class_scanfile_get, + .list = linuxdvb_network_dvbs_class_scanfile_list, + .opts = PO_NOSAVE, + }, {} } }; @@ -96,6 +192,15 @@ const idclass_t linuxdvb_network_atsc_class = .ic_class = "linuxdvb_network_atsc", .ic_caption = "ATSC Network", .ic_properties = (const property_t[]) { + { + .type = PT_STR, + .id = "scanfile", + .name = "Pre-defined Muxes", + .set = linuxdvb_network_class_scanfile_set, + .get = linuxdvb_network_class_scanfile_get, + .list = linuxdvb_network_atsc_class_scanfile_list, + .opts = PO_NOSAVE, + }, {} } }; @@ -194,11 +299,7 @@ linuxdvb_network_create0 htsmsg_t *c, *e; htsmsg_field_t *f; - /* Create */ - if (!(ln = (linuxdvb_network_t*)mpegts_network_create0(calloc(1, sizeof(linuxdvb_network_t)), - idc, uuid, NULL, conf))) - return NULL; - + ln = calloc(1, sizeof(linuxdvb_network_t)); if (idc == &linuxdvb_network_dvbt_class) ln->ln_type = FE_OFDM; else if (idc == &linuxdvb_network_dvbc_class) @@ -207,6 +308,11 @@ linuxdvb_network_create0 ln->ln_type = FE_QPSK; else ln->ln_type = FE_ATSC; + + /* Create */ + if (!(ln = (linuxdvb_network_t*)mpegts_network_create0((void*)ln, + idc, uuid, NULL, conf))) + return NULL; /* Callbacks */ ln->mn_create_mux = linuxdvb_network_create_mux; diff --git a/src/input/mpegts/linuxdvb/scanfile.c b/src/input/mpegts/linuxdvb/scanfile.c new file mode 100644 index 00000000..b4984b8b --- /dev/null +++ b/src/input/mpegts/linuxdvb/scanfile.c @@ -0,0 +1,464 @@ +/* + * tvheadend, intial mux list + * Copyright (C) 2012 Adam Sutton + * + * 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 . + */ + +#include "tvheadend.h" +#include "../dvb.h" +#include "filebundle.h" +#include "config2.h" +#include "scanfile.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +scanfile_region_list_t scanfile_regions_DVBC; +scanfile_region_list_t scanfile_regions_DVBT; +scanfile_region_list_t scanfile_regions_DVBS; +scanfile_region_list_t scanfile_regions_ATSC; + +/* ************************************************************************** + * Country codes + * *************************************************************************/ + +static const struct { + const char *code; + const char *name; +} tldlist[] = { + {"auto", "--Generic--"}, + {"ad", "Andorra"}, + {"ar", "Argentina"}, + {"at", "Austria"}, + {"au", "Australia"}, + {"ax", "Aland Islands"}, + {"be", "Belgium"}, + {"bg", "Bulgaria"}, + {"br", "Brazil"}, + {"ca", "Canada"}, + {"ch", "Switzerland"}, + {"cz", "Czech Republic"}, + {"de", "Germany"}, + {"dk", "Denmark"}, + {"es", "Spain"}, + {"fi", "Finland"}, + {"fr", "France"}, + {"gr", "Greece"}, + {"hk", "Hong Kong"}, + {"hr", "Croatia"}, + {"hu", "Hungary"}, + {"ie", "Ireland"}, + {"il", "Israel"}, + {"ir", "Iran"}, + {"is", "Iceland"}, + {"it", "Italy"}, + {"lt", "Lithuania"}, + {"lu", "Luxembourg"}, + {"lv", "Latvia"}, + {"nl", "Netherlands"}, + {"no", "Norway"}, + {"nz", "New Zealand"}, + {"pl", "Poland"}, + {"pt", "Portugal"}, + {"ro", "Romania"}, + {"ru", "Russia"}, + {"se", "Sweden"}, + {"si", "Slovenia"}, + {"sk", "Slovakia"}, + {"tw", "Taiwan"}, + {"ua", "Ukraine"}, + {"uk", "United Kingdom"}, + {"us", "United States"}, + {"vn", "Vietnam"}, +}; + +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; + return tld; +} + +/* ************************************************************************** + * Type specific parsers + * *************************************************************************/ + +static int +scanfile_load_atsc ( dvb_mux_conf_t *mux, const char *line ) +{ + char qam[20]; + int r; + dvb_frontend_parameters_t *p = &mux->dmc_fe_params; + + r = sscanf(line, "%u %s", &p->frequency, qam); + if (r != 2) return 1; + mux->dmc_fe_delsys = SYS_ATSC; + if ((p->u.vsb.modulation = dvb_str2qam(qam)) == -1) return 1; + + return 0; +} + +static int +scanfile_load_dvbt ( dvb_mux_conf_t *mux, const char *line ) +{ + char bw[20], fec[20], fec2[20], qam[20], mode[20], guard[20], hier[20]; + int r; + uint32_t i; + dvb_frontend_parameters_t *p = &mux->dmc_fe_params; + + if (*line == '2') { + r = sscanf(line+1, "%u %u %u %10s %10s %10s %10s %10s %10s %10s", + &i, &i, &p->frequency, bw, fec, fec2, qam, mode, guard, hier); + if(r != 10) return 1; + mux->dmc_fe_delsys = SYS_DVBT2; + } else { + r = sscanf(line, "%u %10s %10s %10s %10s %10s %10s %10s", + &p->frequency, bw, fec, fec2, qam, mode, guard, hier); + if(r != 8) return 1; + mux->dmc_fe_delsys = SYS_DVBT; + } + + if ((p->u.ofdm.bandwidth = dvb_str2bw(bw)) == -1) return 1; + if ((p->u.ofdm.constellation = dvb_str2qam(qam)) == -1) return 1; + if ((p->u.ofdm.code_rate_HP = dvb_str2fec(fec)) == -1) return 1; + if ((p->u.ofdm.code_rate_LP = dvb_str2fec(fec2)) == -1) return 1; + if ((p->u.ofdm.transmission_mode = dvb_str2mode(mode)) == -1) return 1; + if ((p->u.ofdm.guard_interval = dvb_str2guard(guard)) == -1) return 1; + if ((p->u.ofdm.hierarchy_information = dvb_str2hier(hier)) == -1) return 1; + + return 0; +} + +static int +scanfile_load_dvbs ( dvb_mux_conf_t *mux, const char *line ) +{ + char pol[20], fec[20], qam[20], rolloff[20]; + int r, v2 = 0; + dvb_frontend_parameters_t *p = &mux->dmc_fe_params; + + if (*line == '2') { + v2 = 2; + line++; + } + + r = sscanf(line, "%u %s %u %s %s %s", + &p->frequency, pol, &p->u.qpsk.symbol_rate, + fec, rolloff, qam); + if (r < (4+v2)) return 1; + + if ((mux->dmc_fe_polarisation = dvb_str2pol(pol)) == -1) return 1; + if ((p->u.qpsk.fec_inner = dvb_str2fec(fec)) == -1) return 1; + if (v2) { + mux->dmc_fe_delsys = SYS_DVBS2; + if ((mux->dmc_fe_rolloff = dvb_str2rolloff(rolloff)) == -1) return 1; + if ((mux->dmc_fe_modulation = dvb_str2qam(qam)) == -1) return 1; + } else { + mux->dmc_fe_delsys = SYS_DVBS; + mux->dmc_fe_rolloff = ROLLOFF_35; + mux->dmc_fe_modulation = QPSK; + } + + return 0; +} + +static int +scanfile_load_dvbc ( dvb_mux_conf_t *mux, const char *line ) +{ + char fec[20], qam[20]; + int r; + dvb_frontend_parameters_t *p = &mux->dmc_fe_params; + + r = sscanf(line, "%u %u %s %s", + &p->frequency, &p->u.qam.symbol_rate, fec, qam); + if(r != 4) return 1; + + mux->dmc_fe_delsys = SYS_DVBC_ANNEX_AC; + if ((p->u.qam.fec_inner = dvb_str2fec(fec)) == -1) return 1; + if ((p->u.qam.modulation = dvb_str2qam(qam)) == -1) return 1; + + return 0; +} + + +/* ************************************************************************** + * File processing + * *************************************************************************/ + +/* + * Sorting + */ +static int +scanfile_network_cmp + ( scanfile_network_t *a, scanfile_network_t *b ) +{ + return strcmp(a->sfn_name, b->sfn_name); +} +static int +scanfile_region_cmp + ( scanfile_region_t *a, scanfile_region_t *b ) +{ + return strcmp(a->sfr_name, b->sfr_name); +} + +/* + * Create/Find region entry + * + * TODO: not sure why I didn't use RB here! + */ +static scanfile_region_t * +scanfile_region_create + ( const char *type, const char *id, const char *desc ) +{ + scanfile_region_t *reg; + scanfile_region_list_t *list = NULL; + if (!strcmp(type, "dvb-s")) list = &scanfile_regions_DVBS; + else if (!strcmp(type, "dvb-t")) list = &scanfile_regions_DVBT; + else if (!strcmp(type, "dvb-c")) list = &scanfile_regions_DVBC; + else if (!strcmp(type, "atsc")) list = &scanfile_regions_ATSC; + if (!list) return NULL; + + LIST_FOREACH(reg, list, sfr_link) { + if (!strcmp(reg->sfr_id, id)) break; + } + + if (!reg) { + tvhtrace("scanfile", "%s region %s created", type, id); + reg = calloc(1, sizeof(scanfile_region_t)); + reg->sfr_id = strdup(id); + reg->sfr_name = strdup(desc); + LIST_INSERT_SORTED(list, reg, sfr_link, scanfile_region_cmp); + } + + return reg; +} + +/* + * Process mux entry + */ +static void +scanfile_load_one ( scanfile_network_t *net, const char *line ) +{ + int r = 1; + dvb_mux_conf_t *mux = calloc(1, sizeof(dvb_mux_conf_t)); + + switch (line[0]) { + case 'A': + r = scanfile_load_atsc(mux, line+1); + break; + case 'T': + r = scanfile_load_dvbt(mux, line+1); + break; + case 'S': + r = scanfile_load_dvbs(mux, line+1); + break; + case 'C': + r = scanfile_load_dvbc(mux, line+1); + break; + } + + tvhdebug("scanfile", "[%s] %s", line, r ? "FAIL" : "OK"); + if (r) { + free(mux); + } else { + LIST_INSERT_HEAD(&net->sfn_muxes, mux, dmc_link); + } +} + +/* + * Process a file + */ +static void +scanfile_load_file + ( const char *type, fb_dir *dir, const char *name ) +{ + int i; + fb_file *fp; + scanfile_region_t *reg = NULL; + scanfile_network_t *net; + char *str; + char buf[256], buf2[256]; + tvhtrace("scanfile", "load file %s", name); + + fp = fb_open2(dir, name, 1, 0); + if (!fp) return; + + /* Region */ + strncpy(buf, name, sizeof(buf)); + if (!strcmp(type, "dvb-s")) { + reg = scanfile_region_create(type, "geo", "Geo-synchronous Orbit"); + } else { + str = buf; + while (*str) { + if (*str == '-') { + *str = '\0'; + reg = scanfile_region_create(type, buf, tldcode2longname(buf)); + *str = '-'; + break; + } + str++; + } + } + if (!reg) { + fb_close(fp); + return; + } + + /* Network */ + str = buf; + while (*str) { + if (!isalnum(*str)) *str = '_'; + str++; + } + *str = '\0'; + snprintf(buf2, sizeof(buf2), "%s_%s", type, buf); + net = calloc(1, sizeof(scanfile_network_t)); + net->sfn_id = strdup(buf2); + net->sfn_name = strdup(buf); + LIST_INSERT_SORTED(®->sfr_networks, net, sfn_link, scanfile_network_cmp); + + /* Process file */ + while (!fb_eof(fp)) { + + /* Get line */ + memset(buf, 0, sizeof(buf)); + if (!fb_gets(fp, buf, sizeof(buf) - 1)) break; + i = 0; + while (buf[i]) { + if (buf[i] == '#') + buf[i] = '\0'; + else + i++; + } + while (i > 0 && buf[i-1] < 32) buf[--i] = 0; + + /* Process mux */ + switch (*buf) { + case 'A': + case 'C': + case 'T': + case 'S': + scanfile_load_one(net, buf); + default: + break; + } + } + fb_close(fp); +} + +/* + * Process directory + * + * Note: should we follow symlinks? + */ +static void +scanfile_load_dir + ( const char *path, const char *type, int lvl ) +{ + char p[256]; + fb_dir *dir; + fb_dirent *de; + tvhtrace("scanfile", "load dir %s", path); + + if (lvl >= 3) return; + if (!(dir = fb_opendir(path))) return; + lvl++; + + while ((de = fb_readdir(dir))) { + if (*de->name == '.') continue; + if (de->type == FB_DIR) { + snprintf(p, sizeof(p), "%s/%s", path, de->name); + scanfile_load_dir(p, de->name, lvl+1); + } else if (type) { + scanfile_load_file(type, dir, de->name); + } + } + + fb_closedir(dir); +} + +/* + * Initialise the mux list + */ +void +scanfile_init ( void ) +{ + const char *path = config_get_muxconfpath(); + if (!path || !*path) +#if ENABLE_DVBSCAN + path = "data/dvb-scan"; +#else + path = "/usr/share/dvb"; +#endif + scanfile_load_dir(path, NULL, 0); +} + +/* + * Find scanfile + */ +scanfile_network_t * +scanfile_find ( const char *id ) +{ + char *tok, *s = NULL, *tmp; + scanfile_region_t *r = NULL; + scanfile_network_t *n = NULL; + scanfile_region_list_t *l; + tmp = strdup(id); + + /* Type */ + if (!(tok = strtok_r(tmp, "/", &s))) + return NULL; + printf("tok = %s\n", tok); + if (!strcasecmp(tok, "dvbt")) + l = &scanfile_regions_DVBT; + else if (!strcasecmp(tok, "dvbc")) + l = &scanfile_regions_DVBC; + else if (!strcasecmp(tok, "dvbs")) + l = &scanfile_regions_DVBS; + else if (!strcasecmp(tok, "atsc")) + l = &scanfile_regions_ATSC; + else + return NULL; + + /* Region */ + if (!(tok = strtok_r(NULL, "/", &s))) + return NULL; + printf("tok = %s\n", tok); + LIST_FOREACH(r, l, sfr_link) + if (!strcmp(r->sfr_id, tok)) + break; + if (!r) return NULL; + + /* Network */ + if (!(tok = strtok_r(NULL, "/", &s))) + return NULL; + printf("tok = %s\n", tok); + LIST_FOREACH(n, &r->sfr_networks, sfn_link) + if (!strcmp(n->sfn_id, tok)) + break; + + return n; +} diff --git a/src/input/mpegts/linuxdvb/scanfile.h b/src/input/mpegts/linuxdvb/scanfile.h new file mode 100644 index 00000000..cda2da76 --- /dev/null +++ b/src/input/mpegts/linuxdvb/scanfile.h @@ -0,0 +1,46 @@ +/* + * tvheadend, intial mux list + * Copyright (C) 2012 Adam Sutton + * + * 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 . + */ + +#ifndef __LINUXDVB_SCANFILES_H__ +#define __LINUXDVB_SCANFILES_H__ + +typedef struct scanfile_network { + const char *sfn_id; + const char *sfn_name; + LIST_ENTRY(scanfile_network) sfn_link; + LIST_HEAD(,dvb_mux_conf) sfn_muxes; +} scanfile_network_t; + +typedef struct scanfile_region { + const char *sfr_id; + const char *sfr_name; + LIST_ENTRY(scanfile_region) sfr_link; + LIST_HEAD(,scanfile_network) sfr_networks; +} scanfile_region_t; + +typedef LIST_HEAD(,scanfile_region) scanfile_region_list_t; +extern scanfile_region_list_t scanfile_regions_DVBC; +extern scanfile_region_list_t scanfile_regions_DVBT; +extern scanfile_region_list_t scanfile_regions_DVBS; +extern scanfile_region_list_t scanfile_regions_ATSC; + +void scanfile_init ( void ); + +scanfile_network_t *scanfile_find ( const char *id ); + +#endif /* __LINUXDVB_SCANFILES_H__ */ diff --git a/src/input/mpegts/mpegts_mux.c b/src/input/mpegts/mpegts_mux.c index 06c88f92..c994cfb1 100644 --- a/src/input/mpegts/mpegts_mux.c +++ b/src/input/mpegts/mpegts_mux.c @@ -319,11 +319,7 @@ mpegts_mux_start mm->mm_create_instances(mm); if (!LIST_FIRST(&mm->mm_instances)) { tvhtrace("mpegts", "%s - has no instances", buf); - return SM_CODE_TUNING_FAILED; - // Note: we report a permanent inability to tune at this - // time, rather than a lack of free tuners - // this stops the init scan thinking we can't - // proceed + return SM_CODE_NO_VALID_ADAPTER; } /* Find */ diff --git a/src/input/mpegts/mpegts_network.c b/src/input/mpegts/mpegts_network.c index 9b1905a7..86f1ea52 100644 --- a/src/input/mpegts/mpegts_network.c +++ b/src/input/mpegts/mpegts_network.c @@ -300,8 +300,6 @@ mpegts_network_create0 /* Setup idnode */ idnode_insert(&mn->mn_id, uuid, idc); - if (conf) - idnode_load(&mn->mn_id, conf); /* Default callbacks */ mn->mn_display_name = mpegts_network_display_name; @@ -311,9 +309,6 @@ mpegts_network_create0 mn->mn_mux_class = mpegts_network_mux_class; mn->mn_mux_create2 = mpegts_network_mux_create2; - /* Network name */ - if (netname) mn->mn_network_name = strdup(netname); - /* Init Qs */ TAILQ_INIT(&mn->mn_initial_scan_pending_queue); TAILQ_INIT(&mn->mn_initial_scan_current_queue); @@ -321,6 +316,12 @@ mpegts_network_create0 /* Add to global list */ LIST_INSERT_HEAD(&mpegts_network_all, mn, mn_global_link); + /* Load config */ + if (conf) + idnode_load(&mn->mn_id, conf); + + /* Name */ + if (netname) mn->mn_network_name = strdup(netname); mn->mn_display_name(mn, buf, sizeof(buf)); tvhtrace("mpegts", "created network %s", buf); diff --git a/src/main.c b/src/main.c index 716f4086..3767ecc2 100644 --- a/src/main.c +++ b/src/main.c @@ -54,7 +54,6 @@ #include "service.h" #include "trap.h" #include "settings.h" -#include "muxes.h" #include "config2.h" #include "idnode.h" #include "imagecache.h" diff --git a/src/muxes.c b/src/muxes.c deleted file mode 100644 index 6783fd85..00000000 --- a/src/muxes.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * tvheadend, intial mux list - * Copyright (C) 2012 Adam Sutton - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tvheadend.h" -#include "dvb/dvb.h" -#include "muxes.h" -#include "filebundle.h" -#include "config2.h" - -region_list_t regions_DVBC; -region_list_t regions_DVBT; -region_list_t regions_DVBS; -region_list_t regions_ATSC; - -/* ************************************************************************** - * Country codes - * *************************************************************************/ - -static const struct { - const char *code; - const char *name; - -} tldlist[] = { - {"auto", "--Generic--"}, - {"ad", "Andorra"}, - {"ar", "Argentina"}, - {"at", "Austria"}, - {"au", "Australia"}, - {"ax", "Aland Islands"}, - {"be", "Belgium"}, - {"br", "Brazil"}, - {"ca", "Canada"}, - {"ch", "Switzerland"}, - {"cz", "Czech Republic"}, - {"de", "Germany"}, - {"dk", "Denmark"}, - {"es", "Spain"}, - {"fi", "Finland"}, - {"fr", "France"}, - {"gr", "Greece"}, - {"hk", "Hong Kong"}, - {"hr", "Croatia"}, - {"hu", "Hungary"}, - {"ie", "Ireland"}, - {"il", "Israel"}, - {"ir", "Iran"}, - {"is", "Iceland"}, - {"it", "Italy"}, - {"lt", "Lithuania"}, - {"lu", "Luxembourg"}, - {"lv", "Latvia"}, - {"nl", "Netherlands"}, - {"no", "Norway"}, - {"nz", "New Zealand"}, - {"pl", "Poland"}, - {"ro", "Romania"}, - {"se", "Sweden"}, - {"si", "Slovenia"}, - {"sk", "Slovakia"}, - {"tw", "Taiwan"}, - {"uk", "United Kingdom"}, - {"us", "United States"}, - {"vn", "Vietnam"}, -}; - -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; - return tld; -} - -/* ************************************************************************** - * Type specific parsers - * *************************************************************************/ - -static int _muxes_load_atsc ( mux_t *mux, const char *line ) -{ - char qam[20]; - int r; - - r = sscanf(line, "%u %s", &mux->freq, qam); - if (r != 2) return 1; - if ((mux->constellation = dvb_mux_str2qam(qam)) == -1) return 1; - - return 0; -} - -static int _muxes_load_dvbt ( mux_t *mux, const char *line ) -{ - char bw[20], fec[20], fec2[20], qam[20], mode[20], guard[20], hier[20]; - int r; - uint32_t i; - - if (*line == '2') { - r = sscanf(line+1, "%u %u %u %10s %10s %10s %10s %10s %10s %10s", - &i, &i, &mux->freq, bw, fec, fec2, qam, mode, guard, hier); - if(r != 10) return 1; - } else { - r = sscanf(line, "%u %10s %10s %10s %10s %10s %10s %10s", - &mux->freq, bw, fec, fec2, qam, mode, guard, hier); - if(r != 8) return 1; - } - - if ((mux->bw = dvb_mux_str2bw(bw)) == -1) return 1; - if ((mux->constellation = dvb_mux_str2qam(qam)) == -1) return 1; - if ((mux->fechp = dvb_mux_str2fec(fec)) == -1) return 1; - if ((mux->feclp = dvb_mux_str2fec(fec2)) == -1) return 1; - if ((mux->tmode = dvb_mux_str2mode(mode)) == -1) return 1; - if ((mux->guard = dvb_mux_str2guard(guard)) == -1) return 1; - if ((mux->hierarchy = dvb_mux_str2hier(hier)) == -1) return 1; - - return 0; -} - -static int _muxes_load_dvbs ( mux_t *mux, const char *line ) -{ - char fec[20], qam[20], hier[20]; - int r, v2 = 0; - - if (*line == '2') { - v2 = 2; - line++; - } - - r = sscanf(line, "%u %c %u %s %s %s", - &mux->freq, &mux->polarisation, &mux->symrate, - fec, hier, qam); - if (r != (4+v2)) return 1; - - if ((mux->fec = dvb_mux_str2fec(fec)) == -1) return 1; - if (v2) { - if ((mux->hierarchy = dvb_mux_str2hier(hier)) == -1) return 1; - if ((mux->constellation = dvb_mux_str2qam(qam)) == -1) return 1; - } - - return 0; -} - -static int _muxes_load_dvbc ( mux_t *mux, const char *line ) -{ - char fec[20], qam[20]; - int r; - - r = sscanf(line, "%u %u %s %s", - &mux->freq, &mux->symrate, fec, qam); - if(r != 4) return 1; - - if ((mux->fec = dvb_mux_str2fec(fec)) == -1) return 1; - if ((mux->constellation = dvb_mux_str2qam(qam)) == -1) return 1; - - return 0; -} - - -/* ************************************************************************** - * File processing - * *************************************************************************/ - -/* - * Sorting - */ -static int _net_cmp ( void *a, void *b ) -{ - return strcmp(((network_t*)a)->name, ((network_t*)b)->name); -} -static int _reg_cmp ( void *a, void *b ) -{ - return strcmp(((region_t*)a)->name, ((region_t*)b)->name); -} - -/* - * Create/Find region entry - */ -static region_t *_muxes_region_create - ( const char *type, const char *id, const char *desc ) -{ - region_t *reg; - region_list_t *list = NULL; - if (!strcmp(type, "dvb-s")) list = ®ions_DVBS; - else if (!strcmp(type, "dvb-t")) list = ®ions_DVBT; - else if (!strcmp(type, "dvb-c")) list = ®ions_DVBC; - else if (!strcmp(type, "atsc")) list = ®ions_ATSC; - if (!list) return NULL; - - LIST_FOREACH(reg, list, link) { - if (!strcmp(reg->id, id)) break; - } - - if (!reg) { - reg = calloc(1, sizeof(region_t)); - reg->id = strdup(id); - reg->name = strdup(desc); - LIST_INSERT_SORTED(list, reg, link, _reg_cmp); - } - - return reg; -} - -/* - * Process mux entry - */ -static void _muxes_load_one ( network_t *net, const char *line ) -{ - int r = 1; - mux_t *mux = calloc(1, sizeof(mux_t)); - - switch (line[0]) { - case 'A': - r = _muxes_load_atsc(mux, line+1); - break; - case 'T': - r = _muxes_load_dvbt(mux, line+1); - break; - case 'S': - r = _muxes_load_dvbs(mux, line+1); - break; - case 'C': - r = _muxes_load_dvbc(mux, line+1); - break; - } - - if (r) { - free(mux); - } else { - LIST_INSERT_HEAD(&net->muxes, mux, link); - } -} - -/* - * Process a file - */ -static void _muxes_load_file - ( const char *type, fb_dir *dir, const char *name ) -{ - int i; - fb_file *fp; - region_t *reg = NULL; - network_t *net; - char *str; - char buf[256], buf2[256]; - - fp = fb_open2(dir, name, 1, 0); - if (!fp) return; - - /* Region */ - strncpy(buf, name, sizeof(buf)); - if (!strcmp(type, "dvb-s")) { - reg = _muxes_region_create(type, "geo", "Geo-synchronous Orbit"); - } else { - str = buf; - while (*str) { - if (*str == '-') { - *str = '\0'; - reg = _muxes_region_create(type, buf, tldcode2longname(buf)); - *str = '-'; - break; - } - str++; - } - } - if (!reg) { - fb_close(fp); - return; - } - - /* Network */ - str = buf; - while (*str) { - if (!isalnum(*str)) *str = '_'; - str++; - } - *str = '\0'; - snprintf(buf2, sizeof(buf2), "%s_%s", type, buf); - net = calloc(1, sizeof(network_t)); - net->id = strdup(buf2); - net->name = strdup(buf); - LIST_INSERT_SORTED(®->networks, net, link, _net_cmp); - - /* Process file */ - while (!fb_eof(fp)) { - - /* Get line */ - memset(buf, 0, sizeof(buf)); - if (!fb_gets(fp, buf, sizeof(buf) - 1)) break; - i = 0; - while (buf[i]) { - if (buf[i] == '#') - buf[i] = '\0'; - else - i++; - } - while (i > 0 && buf[i-1] < 32) buf[--i] = 0; - - /* Process mux */ - switch (*buf) { - case 'A': - case 'C': - case 'T': - case 'S': - _muxes_load_one(net, buf); - default: - break; - } - } - fb_close(fp); -} - -/* - * Process directory - * - * Note: should we follow symlinks? - */ -static void _muxes_load_dir - ( const char *path, const char *type, int lvl ) -{ - char p[256]; - fb_dir *dir; - fb_dirent *de; - - if (lvl >= 3) return; - if (!(dir = fb_opendir(path))) return; - lvl++; - - while ((de = fb_readdir(dir))) { - if (*de->name == '.') continue; - if (de->type == FB_DIR) { - snprintf(p, sizeof(p), "%s/%s", path, de->name); - _muxes_load_dir(p, de->name, lvl+1); - } else if (type) { - _muxes_load_file(type, dir, de->name); - } - } - - fb_closedir(dir); -} - -/* - * Initialise the mux list - */ -void muxes_init ( void ) -{ - const char *path = config_get_muxconfpath(); - if (!path || !*path) -#if ENABLE_DVBSCAN - path = "data/dvb-scan"; -#else - path = "/usr/share/dvb"; -#endif - _muxes_load_dir(path, NULL, 0); -} diff --git a/src/muxes.h b/src/muxes.h deleted file mode 100644 index eda77e08..00000000 --- a/src/muxes.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * tvheadend, intial mux list - * Copyright (C) 2012 Adam Sutton - * - * 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 . - */ - -#ifndef __TVH_MUXES_H__ -#define __TVH_MUXES_H__ - -typedef struct mux { - LIST_ENTRY(mux) link; - unsigned int freq; - unsigned int symrate; - short fec; - short constellation; - short bw; - short fechp; - short feclp; - short tmode; - short guard; - short hierarchy; - char polarisation; -} mux_t; - -typedef struct network { -const char *id; -const char *name; -LIST_ENTRY(network) link; -LIST_HEAD(,mux) muxes; -} network_t; - -typedef struct region { -const char *id; -const char *name; -LIST_ENTRY(region) link; -LIST_HEAD(,network) networks; -} region_t; - -typedef LIST_HEAD(,region) region_list_t; -extern region_list_t regions_DVBC; -extern region_list_t regions_DVBT; -extern region_list_t regions_DVBS; -extern region_list_t regions_ATSC; - -void muxes_init ( void ); - -#endif /* __TVH_MUXES_H__ */