362 lines
8.3 KiB
C
362 lines
8.3 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <stdlib.h>
|
|
#include <libgen.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <ctype.h>
|
|
|
|
#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"},
|
|
{"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;
|
|
|
|
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) 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 )
|
|
{
|
|
char p[256];
|
|
fb_dir *dir;
|
|
fb_dirent *de;
|
|
|
|
if (!(dir = fb_opendir(path))) return;
|
|
|
|
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);
|
|
} 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) path = "data/dvb-scan";
|
|
_muxes_load_dir(path, NULL);
|
|
}
|