From 7da7006af6a6aa9a29d5208ec98add5ffb55bd56 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 19 Sep 2013 22:17:02 +0100 Subject: [PATCH] linuxdvb: added pre-defined mux configuration file support This had been left out for too long and makes it awkward for novices to setup the networks. The config is possibly not as nice as before where it was presented as a tree. But this can be improved later. --- Makefile | 1 + src/api/api_mpegts.c | 48 ++ src/api/api_subscriptions.c | 63 +++ src/input/mpegts/dvb.h | 6 +- src/input/mpegts/dvb_support.c | 8 +- src/input/mpegts/linuxdvb/linuxdvb.c | 4 + src/input/mpegts/linuxdvb/linuxdvb_network.c | 116 ++++- src/input/mpegts/linuxdvb/scanfile.c | 464 +++++++++++++++++++ src/input/mpegts/linuxdvb/scanfile.h | 46 ++ src/input/mpegts/mpegts_mux.c | 6 +- src/input/mpegts/mpegts_network.c | 11 +- src/main.c | 1 - src/muxes.c | 381 --------------- src/muxes.h | 59 --- 14 files changed, 753 insertions(+), 461 deletions(-) create mode 100644 src/api/api_subscriptions.c create mode 100644 src/input/mpegts/linuxdvb/scanfile.c create mode 100644 src/input/mpegts/linuxdvb/scanfile.h delete mode 100644 src/muxes.c delete mode 100644 src/muxes.h 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__ */