tvheadend/src/input/mpegts/dvb_psi.c
2014-11-21 15:10:49 +01:00

2454 lines
65 KiB
C
Raw Blame History

/*
* MPEG TS Program Specific Information code
* Copyright (C) 2007 - 2010 Andreas <20>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 "tvheadend.h"
#include "input.h"
#include "dvb.h"
#include "tsdemux.h"
#include "parsers.h"
#include "lang_codes.h"
#include "service.h"
#include "dvb_charset.h"
#include "bouquet.h"
#include "fastscan.h"
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define PRIV_FSAT (('F' << 24) | ('S' << 16) | ('A' << 8) | 'T')
typedef struct dvb_freesat_svc {
TAILQ_ENTRY(dvb_freesat_svc) link;
TAILQ_ENTRY(dvb_freesat_svc) region_link;
uint16_t sid;
uint16_t regionid;
uint16_t lcn;
mpegts_service_t *svc;
} dvb_freesat_svc_t;
typedef struct dvb_freesat_region {
LIST_ENTRY(dvb_freesat_region) link;
TAILQ_HEAD(,dvb_freesat_svc) services;
uint16_t regionid;
char name[52];
bouquet_t *bouquet;
} dvb_freesat_region_t;
typedef struct dvb_bat_svc {
TAILQ_ENTRY(dvb_bat_svc) link;
mpegts_service_t *svc;
uint8_t lcn_dtag;
uint32_t lcn;
dvb_freesat_svc_t *fallback;
int used;
} dvb_bat_svc_t;
typedef struct dvb_bat_id {
LIST_ENTRY(dvb_bat_id) link;
uint32_t complete:1;
uint32_t freesat:1;
uint32_t bskyb:1;
uint16_t nbid;
char name[32];
mpegts_mux_t *mm;
TAILQ_HEAD(,dvb_bat_svc) services;
TAILQ_HEAD(,dvb_freesat_svc) fservices;
LIST_HEAD(,dvb_freesat_region) fregions;
} dvb_bat_id_t;
typedef struct dvb_bat {
int complete;
LIST_HEAD(,dvb_bat_id) bats;
} dvb_bat_t;
SKEL_DECLARE(mpegts_table_state_skel, struct mpegts_table_state);
int dvb_bouquets_parse = 1;
static int
psi_parse_pmt(mpegts_mux_t *mux, mpegts_service_t *t, const uint8_t *ptr, int len);
/* **************************************************************************
* Lookup tables
* *************************************************************************/
static const int dvb_servicetype_map[][2] = {
{ 0x01, ST_SDTV }, /* SDTV (MPEG2) */
{ 0x02, ST_RADIO },
{ 0x11, ST_HDTV }, /* HDTV (MPEG2) */
{ 0x16, ST_SDTV }, /* Advanced codec SDTV */
{ 0x19, ST_HDTV }, /* Advanced codec HDTV */
{ 0x80, ST_SDTV }, /* NET POA - Cabo SDTV */
{ 0x91, ST_HDTV }, /* Bell TV HDTV */
{ 0x96, ST_SDTV }, /* Bell TV SDTV */
{ 0xA0, ST_HDTV }, /* Bell TV tiered HDTV */
{ 0xA4, ST_HDTV }, /* DN HDTV */
{ 0xA6, ST_HDTV }, /* Bell TV tiered HDTV */
{ 0xA8, ST_SDTV }, /* DN advanced SDTV */
{ 0xD3, ST_SDTV }, /* SKY TV SDTV */
};
int
dvb_servicetype_lookup ( int t )
{
int i;
for (i = 0; i < ARRAY_SIZE(dvb_servicetype_map); i++) {
if (dvb_servicetype_map[i][0] == t)
return dvb_servicetype_map[i][1];
}
return -1;
}
static void
dvb_bouquet_comment ( bouquet_t *bq, mpegts_mux_t *mm )
{
char comment[128];
if (bq->bq_comment && bq->bq_comment[0])
return;
free(bq->bq_comment);
mpegts_mux_nice_name(mm, comment, sizeof(comment));
bq->bq_comment = strdup(comment);
bq->bq_saveflag = 1;
}
/* **************************************************************************
* Descriptors
* *************************************************************************/
#if ENABLE_MPEGTS_DVB
/**
* Tables for delivery descriptor parsing
*/
static const dvb_fe_code_rate_t fec_tab [16] = {
DVB_FEC_AUTO, DVB_FEC_1_2, DVB_FEC_2_3, DVB_FEC_3_4,
DVB_FEC_5_6, DVB_FEC_7_8, DVB_FEC_8_9, DVB_FEC_3_5,
DVB_FEC_4_5, DVB_FEC_9_10, DVB_FEC_NONE, DVB_FEC_NONE,
DVB_FEC_NONE, DVB_FEC_NONE, DVB_FEC_NONE, DVB_FEC_NONE
};
/*
* Satellite delivery descriptor
*/
static mpegts_mux_t *
dvb_desc_sat_del
(mpegts_mux_t *mm, uint16_t onid, uint16_t tsid,
const uint8_t *ptr, int len )
{
int frequency, symrate;
dvb_mux_conf_t dmc;
char buf[128];
/* Not enough data */
if(len < 11) return NULL;
/* Extract data */
frequency =
(bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 +
bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3])) * 10;
symrate =
bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 +
bcdtoint(ptr[9]) * 10 + (ptr[10] >> 4);
if (!frequency) {
tvhlog(LOG_WARNING, "nit", "dvb-s frequency error");
return NULL;
}
if (!symrate) {
tvhlog(LOG_WARNING, "nit", "dvb-s symbol rate error");
return NULL;
}
memset(&dmc, 0, sizeof(dmc));
dmc.dmc_fe_type = DVB_TYPE_S;
dmc.dmc_fe_pilot = DVB_PILOT_AUTO;
dmc.dmc_fe_inversion = DVB_INVERSION_AUTO;
dmc.dmc_fe_freq = frequency;
dmc.u.dmc_fe_qpsk.orbital_pos = bcdtoint(ptr[4]) * 100 + bcdtoint(ptr[5]);
dmc.u.dmc_fe_qpsk.orbital_dir = (ptr[6] & 0x80) ? 'E' : 'W';
dmc.u.dmc_fe_qpsk.polarisation = (ptr[6] >> 5) & 0x03;
dmc.u.dmc_fe_qpsk.symbol_rate = symrate * 100;
dmc.u.dmc_fe_qpsk.fec_inner = fec_tab[ptr[10] & 0x0f];
static int mtab[4] = {
DVB_MOD_NONE, DVB_MOD_QPSK, DVB_MOD_PSK_8, DVB_MOD_QAM_16
};
static int rtab[4] = {
DVB_ROLLOFF_35, DVB_ROLLOFF_25, DVB_ROLLOFF_20, DVB_ROLLOFF_AUTO
};
dmc.dmc_fe_delsys = (ptr[6] & 0x4) ? DVB_SYS_DVBS2 : DVB_SYS_DVBS;
dmc.dmc_fe_modulation = mtab[ptr[6] & 0x3];
if (dmc.dmc_fe_modulation != DVB_MOD_NONE &&
dmc.dmc_fe_modulation != DVB_MOD_QPSK)
/* standard DVB-S allows only QPSK */
/* on 13.0E, there are (ptr[6] & 4) == 0 muxes with 8PSK and DVB-S2 */
dmc.dmc_fe_delsys = DVB_SYS_DVBS2;
dmc.dmc_fe_rolloff = rtab[(ptr[6] >> 3) & 0x3];
if (dmc.dmc_fe_delsys == DVB_SYS_DVBS &&
dmc.dmc_fe_rolloff != DVB_ROLLOFF_35) {
tvhwarn("nit", "dvb-s rolloff error");
return NULL;
}
/* Debug */
dvb_mux_conf_str(&dmc, buf, sizeof(buf));
tvhdebug("nit", " %s", buf);
/* Create */
return mm->mm_network->mn_create_mux(mm, onid, tsid, &dmc);
}
/*
* Cable delivery descriptor
*/
static mpegts_mux_t *
dvb_desc_cable_del
(mpegts_mux_t *mm, uint16_t onid, uint16_t tsid,
const uint8_t *ptr, int len )
{
int frequency, symrate;
dvb_mux_conf_t dmc;
char buf[128];
static const dvb_fe_modulation_t qtab [6] = {
DVB_MOD_QAM_AUTO, DVB_MOD_QAM_16, DVB_MOD_QAM_32, DVB_MOD_QAM_64,
DVB_MOD_QAM_128, DVB_MOD_QAM_256
};
/* Not enough data */
if(len < 11) return NULL;
/* Extract data */
frequency =
bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 +
bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3]);
symrate =
bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 +
bcdtoint(ptr[9]) * 10 + (ptr[10] >> 4);
if (!frequency) {
tvhwarn("nit", "dvb-c frequency error");
return NULL;
}
if (!symrate) {
tvhwarn("nit", "dvb-c symbol rate error");
return NULL;
}
memset(&dmc, 0, sizeof(dmc));
dmc.dmc_fe_type = DVB_TYPE_C;
dmc.dmc_fe_delsys = DVB_SYS_DVBC_ANNEX_A;
dmc.dmc_fe_inversion = DVB_INVERSION_AUTO;
dmc.dmc_fe_freq = frequency * 100;
dmc.u.dmc_fe_qam.symbol_rate = symrate * 100;
if((ptr[6] & 0x0f) >= sizeof(qtab))
dmc.dmc_fe_modulation = DVB_MOD_QAM_AUTO;
else
dmc.dmc_fe_modulation = qtab[ptr[6] & 0x0f];
dmc.u.dmc_fe_qam.fec_inner = fec_tab[ptr[10] & 0x07];
/* Debug */
dvb_mux_conf_str(&dmc, buf, sizeof(buf));
tvhdebug("nit", " %s", buf);
/* Create */
return mm->mm_network->mn_create_mux(mm, onid, tsid, &dmc);
}
/*
* Terrestrial delivery descriptor
*/
static mpegts_mux_t *
dvb_desc_terr_del
(mpegts_mux_t *mm, uint16_t onid, uint16_t tsid,
const uint8_t *ptr, int len )
{
static const dvb_fe_bandwidth_t btab [8] = {
DVB_BANDWIDTH_8_MHZ, DVB_BANDWIDTH_7_MHZ,
DVB_BANDWIDTH_6_MHZ, DVB_BANDWIDTH_AUTO,
DVB_BANDWIDTH_AUTO, DVB_BANDWIDTH_AUTO,
DVB_BANDWIDTH_AUTO, DVB_BANDWIDTH_AUTO
};
static const dvb_fe_modulation_t ctab [4] = {
DVB_MOD_QPSK, DVB_MOD_QAM_16, DVB_MOD_QAM_64, DVB_MOD_QAM_AUTO
};
static const dvb_fe_guard_interval_t gtab [4] = {
DVB_GUARD_INTERVAL_1_32, DVB_GUARD_INTERVAL_1_16,
DVB_GUARD_INTERVAL_1_8, DVB_GUARD_INTERVAL_1_4
};
static const dvb_fe_transmit_mode_t ttab [4] = {
DVB_TRANSMISSION_MODE_2K,
DVB_TRANSMISSION_MODE_8K,
DVB_TRANSMISSION_MODE_4K,
DVB_TRANSMISSION_MODE_AUTO
};
static const dvb_fe_hierarchy_t htab [8] = {
DVB_HIERARCHY_NONE, DVB_HIERARCHY_1, DVB_HIERARCHY_2, DVB_HIERARCHY_4,
DVB_HIERARCHY_NONE, DVB_HIERARCHY_1, DVB_HIERARCHY_2, DVB_HIERARCHY_4
};
int frequency;
dvb_mux_conf_t dmc;
char buf[128];
/* Not enough data */
if (len < 11) return NULL;
/* Extract data */
frequency = ((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]);
if (!frequency) {
tvhwarn("nit", "dvb-t frequency error");
return NULL;
}
memset(&dmc, 0, sizeof(dmc));
dmc.dmc_fe_type = DVB_TYPE_T;
dmc.dmc_fe_delsys = DVB_SYS_DVBT;
dmc.dmc_fe_inversion = DVB_INVERSION_AUTO;
dmc.dmc_fe_freq = frequency * 10;
dmc.u.dmc_fe_ofdm.bandwidth = btab[(ptr[4] >> 5) & 0x7];
dmc.dmc_fe_modulation = ctab[(ptr[5] >> 6) & 0x3];
dmc.u.dmc_fe_ofdm.hierarchy_information = htab[(ptr[5] >> 3) & 0x3];
dmc.u.dmc_fe_ofdm.code_rate_HP = fec_tab[(ptr[5] + 1) & 0x7];
dmc.u.dmc_fe_ofdm.code_rate_LP = fec_tab[((ptr[6] + 1) >> 5) & 0x7];
dmc.u.dmc_fe_ofdm.guard_interval = gtab[(ptr[6] >> 3) & 0x3];
dmc.u.dmc_fe_ofdm.transmission_mode = ttab[(ptr[6] >> 1) & 0x3];
/* Debug */
dvb_mux_conf_str(&dmc, buf, sizeof(buf));
tvhdebug("nit", " %s", buf);
/* Create */
return mm->mm_network->mn_create_mux(mm, onid, tsid, &dmc);
}
#endif /* ENABLE_MPEGTS_DVB */
static int
dvb_desc_service
( const uint8_t *ptr, int len, int *stype,
char *sprov, size_t sprov_len,
char *sname, size_t sname_len, const char *charset )
{
int r;
size_t l;
char *str;
if (len < 2)
return -1;
/* Type */
*stype = *ptr++;
/* Provider */
if ((r = dvb_get_string_with_len(sprov, sprov_len, ptr, len, charset, NULL)) < 0)
return -1;
/* Name */
if (dvb_get_string_with_len(sname, sname_len, ptr+r, len-r, charset, NULL) < 0)
return -1;
/* Cleanup name */
str = sname;
while (*str && *str <= 32)
str++;
memmove(sname, str, sname_len); // Note: could avoid this copy by passing an output ptr
l = strlen(str);
while (l > 1 && str[l-1] <= 32) {
str[l-1] = 0;
l--;
}
return 0;
}
static dvb_bat_svc_t *
dvb_bat_find_service( dvb_bat_id_t *bi, mpegts_service_t *s,
uint8_t lcn_dtag, uint32_t lcn )
{
dvb_bat_svc_t *bs;
assert(bi);
TAILQ_FOREACH(bs, &bi->services, link)
if (bs->svc == s)
break;
if (!bs) {
bs = calloc(1, sizeof(*bs));
bs->svc = s;
TAILQ_INSERT_TAIL(&bi->services, bs, link);
}
if (lcn != UINT_MAX && !bs->lcn_dtag) {
bs->lcn_dtag = lcn_dtag;
bs->lcn = lcn;
}
return bs;
}
static int
dvb_desc_service_list
( const char *dstr, const uint8_t *ptr, int len, mpegts_mux_t *mm,
dvb_bat_id_t *bi )
{
uint16_t stype, sid;
mpegts_service_t *s;
int i;
for (i = 0; i < len; i += 3) {
sid = (ptr[i] << 8) | ptr[i+1];
stype = ptr[i+2];
tvhdebug(dstr, " service %04X (%d) type %02X (%d)", sid, sid, stype, stype);
if (mm) {
int save = 0;
s = mpegts_service_find(mm, sid, 0, 1, &save);
if (bi)
dvb_bat_find_service(bi, s, 0, UINT_MAX);
if (save)
s->s_config_save((service_t*)s);
}
}
return 0;
}
static int
dvb_desc_local_channel
( const char *dstr, const uint8_t *ptr, int len,
uint8_t dtag, mpegts_mux_t *mm, dvb_bat_id_t *bi )
{
int save = 0;
uint16_t sid, lcn;
mpegts_service_t *s;
if ((len % 4) != 0)
return 0;
while(len >= 4) {
sid = (ptr[0] << 8) | ptr[1];
lcn = ((ptr[2] & 3) << 8) | ptr[3];
tvhdebug(dstr, " sid %d lcn %d", sid, lcn);
if (sid && lcn && mm) {
s = mpegts_service_find(mm, sid, 0, 0, &save);
if (s) {
if (bi) {
dvb_bat_find_service(bi, s, dtag, lcn);
} else if ((!s->s_dvb_channel_dtag ||
s->s_dvb_channel_dtag == dtag) &&
s->s_dvb_channel_num != lcn) {
s->s_dvb_channel_dtag = dtag;
s->s_dvb_channel_num = lcn;
s->s_config_save((service_t*)s);
service_refresh_channel((service_t*)s);
}
}
}
ptr += 4;
len -= 4;
}
return 0;
}
#if ENABLE_MPEGTS_DVB
/*
* UK FreeSat
*/
static void
dvb_freesat_local_channels
( dvb_bat_id_t *bi, const char *dstr, const uint8_t *ptr, int len )
{
uint16_t sid, lcn, regionid;
#if ENABLE_TRACE
uint16_t unk;
#endif
dvb_freesat_svc_t *fs;
int len2;
while (len > 4) {
sid = (ptr[0] << 8) | ptr[1];
#if ENABLE_TRACE
unk = (ptr[2] << 8) | ptr[3];
#endif
len2 = ptr[4];
ptr += 5;
len -= 5;
if (len2 > len)
break;
#if ENABLE_TRACE
tvhtrace(dstr, " sid %04X (%d) uknown %04X (%d)", sid, sid, unk, unk);
#endif
while (len2 > 3) {
lcn = ((ptr[0] & 0x0f) << 8) | ptr[1];
regionid = (ptr[2] << 8) | ptr[3];
tvhtrace(dstr, " lcn %d region %d", lcn, regionid);
TAILQ_FOREACH(fs, &bi->fservices, link)
if (fs->sid == sid && fs->regionid == regionid)
break;
if (!fs) {
fs = calloc(1, sizeof(*fs));
fs->sid = sid;
fs->regionid = regionid;
fs->lcn = lcn;
TAILQ_INSERT_TAIL(&bi->fservices, fs, link);
}
ptr += 4;
len -= 4;
len2 -= 4;
}
}
}
static void
dvb_freesat_regions
( dvb_bat_id_t *bi, const char *dstr, const uint8_t *ptr, int len )
{
uint16_t id;
char name[32];
dvb_freesat_region_t *fr;
int r;
if (!bi)
return;
while (len > 5) {
id = (ptr[0] << 8) | ptr[1];
/* language: ptr[2-4]: 'eng' */
if ((r = dvb_get_string_with_len(name, sizeof(name), ptr + 5, len - 5, NULL, NULL)) < 0)
break;
tvhtrace(dstr, " region %u - '%s'", id, name);
LIST_FOREACH(fr, &bi->fregions, link)
if (fr->regionid == id)
break;
if (!fr) {
fr = calloc(1, sizeof(*fr));
fr->regionid = id;
strncpy(fr->name, name, sizeof(fr->name)-1);
fr->name[sizeof(fr->name)-1] = '\0';
TAILQ_INIT(&fr->services);
LIST_INSERT_HEAD(&bi->fregions, fr, link);
}
ptr += 5 + r;
len -= 5 + r;
}
}
static int
dvb_freesat_add_service
( dvb_bat_id_t *bi, dvb_freesat_region_t *fr, mpegts_service_t *s, uint32_t lcn )
{
char name[96], src[64];
if (!fr->bouquet) {
strcpy(name, "???");
if (idnode_is_instance(&bi->mm->mm_id, &dvb_mux_dvbs_class))
dvb_sat_position_to_str(dvb_sat_position(&((dvb_mux_t *)bi->mm)->lm_tuning),
name, sizeof(name));
snprintf(src, sizeof(src), "dvb-%s://dvbs,%s,%04X,%u",
bi->freesat ? "freesat" : "bskyb", name, bi->nbid, fr->regionid);
snprintf(name, sizeof(name), "%s: %s", bi->name, fr->name);
fr->bouquet = bouquet_find_by_source(name, src, 1);
}
bouquet_add_service(fr->bouquet, (service_t *)s, (int64_t)lcn * CHANNEL_SPLIT, 0);
return fr->bouquet->bq_enabled;
}
static void
dvb_freesat_completed
( dvb_bat_t *b, dvb_bat_id_t *bi, const char *dstr )
{
dvb_bat_svc_t *bs;
dvb_freesat_svc_t *fs;
dvb_freesat_region_t *fr;
uint16_t sid;
uint32_t total = 0, regions = 0, uregions = 0;
tvhtrace(dstr, "completed %s [%04X] bouquets '%s'",
bi->freesat ? "freesat" : "bskyb", bi->nbid, bi->name);
/* Find all "fallback" services and region specific */
TAILQ_FOREACH(bs, &bi->services, link) {
total++;
sid = bs->svc->s_dvb_service_id;
TAILQ_FOREACH(fs, &bi->fservices, link)
if (fs->sid == sid) {
fs->svc = bs->svc;
if (fs->regionid == 0 || fs->regionid == 0xffff) {
if (fs->regionid != 0 || !bs->fallback)
bs->fallback = fs;
continue;
}
LIST_FOREACH(fr, &bi->fregions, link)
if (fr->regionid == fs->regionid)
break;
if (!fr)
tvhtrace(dstr, "cannot find freesat region id %u", fs->regionid);
else
TAILQ_INSERT_TAIL(&fr->services, fs, region_link);
}
}
/* create bouquets, one per region */
LIST_FOREACH(fr, &bi->fregions, link) {
regions++;
if (TAILQ_EMPTY(&fr->services)) continue;
uregions++;
TAILQ_FOREACH(fs, &fr->services, region_link) {
if (!dvb_freesat_add_service(bi, fr, fs->svc, fs->lcn)) break;
TAILQ_FOREACH(bs, &bi->services, link)
if (bs->fallback && fs->lcn == bs->fallback->lcn)
bs->used = 1;
}
if (fs) continue;
TAILQ_FOREACH(bs, &bi->services, link) {
if (bs->used) {
bs->used = 0;
continue;
}
TAILQ_FOREACH(fs, &fr->services, region_link)
if (fs->svc == bs->svc)
break;
if (fs) continue;
if ((fs = bs->fallback) != NULL) {
if (!dvb_freesat_add_service(bi, fr, bs->svc, fs->lcn)) break;
} else {
if (!dvb_freesat_add_service(bi, fr, bs->svc, 0)) break;
}
}
}
tvhtrace(dstr, "completed %s [%04X] bouquets '%s' total %u regions %u (%u)",
bi->freesat ? "freesat" : "bskyb", bi->nbid, bi->name,
total, regions, uregions);
/* Remove all services associated to region, notify the completed status */
LIST_FOREACH(fr, &bi->fregions, link) {
TAILQ_INIT(&fr->services);
if (fr->bouquet) {
dvb_bouquet_comment(fr->bouquet, bi->mm);
bouquet_completed(fr->bouquet, total);
fr->bouquet = NULL;
}
}
/* Clear all "fallback/default" services */
TAILQ_FOREACH(bs, &bi->services, link)
bs->fallback = NULL;
tvhtrace(dstr, "completed %s [%04X] bouquets '%s' update finished",
bi->freesat ? "freesat" : "bskyb", bi->nbid, bi->name);
}
/*
* UK BSkyB
*/
static struct strtab bskyb_regions[] = {
{ "Atherstone", 19 },
{ "Border England", 12 },
{ "Border Scotland", 36 },
{ "Brighton", 65 },
{ "Central Midlands", 3 },
{ "Channel Isles", 34 },
{ "Dundee", 39 },
{ "East Midlands", 20 },
{ "Essex", 2 },
{ "Gloucester", 24 },
{ "Grampian", 35 },
{ "Granada", 7 },
{ "Henley On Thames", 70 },
{ "HTV Wales", 43 },
{ "HTV West", 4 },
{ "HTV West / Thames Valley", 63 },
{ "Humber", 29 },
{ "London", 1 },
{ "London / Essex", 18 },
{ "London / Thames Valley", 66 },
{ "London Kent", 64 },
{ "Meridian East", 11 },
{ "Meridian North", 68 },
{ "Meridian South", 5 },
{ "Meridian South East", 10 },
{ "Merseyside", 45 },
{ "Norfolk", 21 },
{ "North East Midlands", 62 },
{ "North West Yorkshire", 8 },
{ "North Yorkshire", 26 },
{ "Northern Ireland", 33 },
{ "Oxford", 71 },
{ "Republic of Ireland", 50 },
{ "Ridge Hill", 41 },
{ "Scarborough", 61 },
{ "Scottish East", 37 },
{ "Scottish West", 38 },
{ "Sheffield", 60 },
{ "South Lakeland", 28 },
{ "South Yorkshire", 72 },
{ "Tees", 69 },
{ "Thames Valley", 9 },
{ "Tring", 27 },
{ "Tyne", 13 },
{ "West Anglia", 25 },
{ "West Dorset", 67 },
{ "Westcountry", 6 },
};
static void
dvb_bskyb_local_channels
( dvb_bat_id_t *bi, const char *dstr,
const uint8_t *ptr, int len, mpegts_mux_t *mm )
{
uint16_t sid, lcn, regionid;
#if ENABLE_TRACE
uint16_t unk, stype;
#endif
dvb_freesat_region_t *fr;
dvb_freesat_svc_t *fs;
dvb_bat_svc_t *bs;
mpegts_service_t *s;
const char *str;
char buf[16];
if (len < 2)
return;
regionid = (ptr[1] != 0xff) ? ptr[1] : 0xffff;
#if 0
if (regionid != 0xffff && regionid != 0 && regionid != 1) {
if ((str = getenv("TVHEADEND_BSKYB_REGIONID")) != NULL) {
if (regionid != atoi(str))
return;
} else {
return;
}
}
#endif
len -= 2;
ptr += 2;
tvhtrace(dstr, " region id %04X (%d) unknown %02X (%d)",
regionid, regionid, ptr[0], ptr[0]);
while (len > 8) {
sid = (ptr[0] << 8) | ptr[1];
lcn = (ptr[5] << 8) | ptr[6];
#if ENABLE_TRACE
stype = ptr[2];
unk = (ptr[3] << 8) | ptr[4];
#endif
ptr += 9;
len -= 9;
#if ENABLE_TRACE
tvhtrace(dstr, " sid %04X (%d) type %02X (%d) lcn %d unknown %04X (%d)",
sid, sid, stype, stype, lcn, unk, unk);
#endif
TAILQ_FOREACH(fs, &bi->fservices, link)
if (fs->sid == sid && fs->regionid == regionid)
break;
if (!fs) {
fs = calloc(1, sizeof(*fs));
fs->sid = sid;
fs->regionid = regionid;
fs->lcn = lcn != 0xffff ? lcn : 0;
TAILQ_INSERT_TAIL(&bi->fservices, fs, link);
}
TAILQ_FOREACH(bs, &bi->services, link)
if (bs->svc->s_dvb_service_id == sid)
break;
if (mm && !bs) {
s = mpegts_service_find(mm, sid, 0, 0, NULL);
if (s) {
bs = calloc(1, sizeof(*bs));
bs->svc = s;
TAILQ_INSERT_TAIL(&bi->services, bs, link);
}
}
if (regionid && regionid != 0xffff) {
LIST_FOREACH(fr, &bi->fregions, link)
if (fr->regionid == regionid)
break;
if (!fr) {
fr = calloc(1, sizeof(*fr));
fr->regionid = regionid;
/* Note: Poland provider on 13E uses also this bouquet format */
if (bi->nbid < 0x1000 || bi->nbid > 0x1010 ||
(str = val2str(regionid, bskyb_regions)) == NULL) {
snprintf(buf, sizeof(buf), "Region %d", regionid);
str = buf;
}
strncpy(fr->name, str, sizeof(fr->name)-1);
fr->name[sizeof(fr->name)-1] = '\0';
TAILQ_INIT(&fr->services);
LIST_INSERT_HEAD(&bi->fregions, fr, link);
}
}
}
}
#endif /* ENABLE_MPEGTS_DVB */
/* **************************************************************************
* Tables
* *************************************************************************/
static int sect_cmp
( struct mpegts_table_state *a, struct mpegts_table_state *b )
{
if (a->tableid != b->tableid)
return a->tableid - b->tableid;
if (a->extraid < b->extraid)
return -1;
if (a->extraid > b->extraid)
return 1;
return 0;
}
static void
mpegts_table_state_reset
( mpegts_table_t *mt, mpegts_table_state_t *st, int last )
{
int i;
mt->mt_finished = 0;
st->complete = 0;
st->version = 0xff; /* invalid */
memset(st->sections, 0, sizeof(st->sections));
for (i = 0; i < last / 32; i++)
st->sections[i] = 0xFFFFFFFF;
st->sections[last / 32] = 0xFFFFFFFF << (31 - (last % 32));
}
static struct mpegts_table_state *
mpegts_table_state_find
( mpegts_table_t *mt, int tableid, uint64_t extraid, int last )
{
struct mpegts_table_state *st;
/* Find state */
SKEL_ALLOC(mpegts_table_state_skel);
mpegts_table_state_skel->tableid = tableid;
mpegts_table_state_skel->extraid = extraid;
st = RB_INSERT_SORTED(&mt->mt_state, mpegts_table_state_skel, link, sect_cmp);
if (!st) {
st = mpegts_table_state_skel;
SKEL_USED(mpegts_table_state_skel);
mt->mt_incomplete++;
mpegts_table_state_reset(mt, st, last);
}
return st;
}
/*
* End table
*/
static int
dvb_table_complete
(mpegts_table_t *mt)
{
if (mt->mt_incomplete || !mt->mt_complete) {
int total = 0;
mpegts_table_state_t *st;
RB_FOREACH(st, &mt->mt_state, link)
total++;
tvhtrace(mt->mt_name, "incomplete %d complete %d total %d",
mt->mt_incomplete, mt->mt_complete, total);
return 2;
}
if (!mt->mt_finished)
tvhdebug(mt->mt_name, "completed pid %d table %08X / %08x", mt->mt_pid, mt->mt_table, mt->mt_mask);
mt->mt_finished = 1;
return 0;
}
int
dvb_table_end
(mpegts_table_t *mt, mpegts_table_state_t *st, int sect)
{
int sa, sb;
uint32_t rem;
if (st && !st->complete) {
assert(sect >= 0 && sect <= 255);
sa = sect / 32;
sb = sect % 32;
st->sections[sa] &= ~(0x1 << (31 - sb));
if (!st->sections[sa]) {
rem = 0;
for (sa = 0; sa < 8; sa++)
rem |= st->sections[sa];
if (rem) return 1;
tvhtrace(mt->mt_name, " tableid %02X extraid %016" PRIx64 " completed",
st->tableid, st->extraid);
st->complete = 1;
mt->mt_incomplete--;
return dvb_table_complete(mt);
}
} else if (st)
return dvb_table_complete(mt);
return 2;
}
/*
* Begin table
*/
int
dvb_table_begin
(mpegts_table_t *mt, const uint8_t *ptr, int len,
int tableid, uint64_t extraid, int minlen,
mpegts_table_state_t **ret, int *sect, int *last, int *ver)
{
mpegts_table_state_t *st;
uint32_t sa, sb;
/* Not long enough */
if (len < minlen) {
tvhdebug(mt->mt_name, "invalid table length %d min %d", len, minlen);
return -1;
}
/* Ignore next */
if((ptr[2] & 1) == 0)
return -1;
tvhtrace(mt->mt_name, "pid %02X tableid %02X extraid %016" PRIx64 " len %d",
mt->mt_pid, tableid, extraid, len);
/* Section info */
if (sect && ret) {
*sect = ptr[3];
*last = ptr[4];
*ver = (ptr[2] >> 1) & 0x1F;
*ret = st = mpegts_table_state_find(mt, tableid, extraid, *last);
tvhtrace(mt->mt_name, " section %d last %d ver %d (ver %d st %d incomp %d comp %d)",
*sect, *last, *ver, st->version, st->complete, mt->mt_incomplete, mt->mt_complete);
/* New version */
if (st->version != *ver) {
if (st->complete == 2)
mt->mt_complete--;
if (st->complete)
mt->mt_incomplete++;
tvhtrace(mt->mt_name, " new version, restart");
mpegts_table_state_reset(mt, st, *last);
st->version = *ver;
}
/* Complete? */
if (st->complete) {
tvhtrace(mt->mt_name, " skip, already complete (%i)", st->complete);
if (st->complete == 1) {
st->complete = 2;
mt->mt_complete++;
return dvb_table_complete(mt);
} else if (st->complete == 2) {
return dvb_table_complete(mt);
}
assert(0);
}
/* Already seen? */
sa = *sect / 32;
sb = *sect % 32;
if (!(st->sections[sa] & (0x1 << (31 - sb)))) {
tvhtrace(mt->mt_name, " skip, already seen");
return -1;
}
}
tvhlog_hexdump(mt->mt_name, ptr, len);
return 1;
}
void
dvb_table_reset(mpegts_table_t *mt)
{
mpegts_table_state_t *st;
tvhtrace(mt->mt_name, "pid %02X complete reset", mt->mt_pid);
mt->mt_incomplete = 0;
mt->mt_complete = 0;
while ((st = RB_FIRST(&mt->mt_state)) != NULL) {
RB_REMOVE(&mt->mt_state, st, link);
free(st);
}
}
/*
* PAT processing
*/
int
dvb_pat_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
int r, sect, last, ver;
uint16_t sid, pid, tsid;
uint16_t nit_pid = 0;
mpegts_mux_t *mm = mt->mt_mux;
mpegts_table_state_t *st = NULL;
mpegts_service_t *s;
/* Begin */
if (tableid != 0) return -1;
tsid = (ptr[0] << 8) | ptr[1];
r = dvb_table_begin(mt, ptr, len, tableid, tsid, 5,
&st, &sect, &last, &ver);
if (r != 1) return r;
/* Multiplex */
tvhdebug("pat", "tsid %04X (%d)", tsid, tsid);
mpegts_mux_set_tsid(mm, tsid, 1);
/* Process each programme */
ptr += 5;
len -= 5;
while(len >= 4) {
sid = ptr[0] << 8 | ptr[1];
pid = (ptr[2] & 0x1f) << 8 | ptr[3];
/* NIT PID */
if (sid == 0) {
if (pid) {
nit_pid = pid;
tvhdebug("pat", " nit on pid %04X (%d)", pid, pid);
}
/* Service */
} else if (pid) {
tvhdebug("pat", " sid %04X (%d) on pid %04X (%d)", sid, sid, pid, pid);
int save = 0;
if ((s = mpegts_service_find(mm, sid, pid, 1, &save))) {
mpegts_table_add(mm, DVB_PMT_BASE, DVB_PMT_MASK, dvb_pmt_callback,
NULL, "pmt", MT_CRC | MT_QUICKREQ | MT_SCANSUBS,
pid);
if (save)
service_request_save((service_t*)s, 1);
}
}
/* Next */
ptr += 4;
len -= 4;
}
/* Install NIT handler */
if (nit_pid)
mpegts_table_add(mm, DVB_NIT_BASE, DVB_NIT_MASK, dvb_nit_callback,
NULL, "nit", MT_QUICKREQ | MT_CRC, nit_pid);
/* End */
return dvb_table_end(mt, st, sect);
}
/*
* CAT processing
*/
int
dvb_cat_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
int r, sect, last, ver;
uint8_t dtag, dlen;
uint16_t pid;
uintptr_t caid;
mpegts_mux_t *mm = mt->mt_mux;
mpegts_table_state_t *st = NULL;
/* Start */
r = dvb_table_begin(mt, ptr, len, tableid, 0, 5, &st, &sect, &last, &ver);
if (r != 1) return r;
ptr += 5;
len -= 5;
/* Send CAT data for descramblers */
descrambler_cat_data(mm, ptr, len);
while(len > 2) {
dtag = *ptr++;
dlen = *ptr++;
len -= 2;
switch(dtag) {
case DVB_DESC_CA:
if (len >= 4 && dlen >= 4) {
caid = ( ptr[0] << 8) | ptr[1];
pid = ((ptr[2] & 0x1f) << 8) | ptr[3];
tvhdebug("cat", " caid %04X (%d) pid %04X (%d)",
(uint16_t)caid, (uint16_t)caid, pid, pid);
}
break;
default:
break;
}
ptr += dlen;
len -= dlen;
}
/* Finish */
return dvb_table_end(mt, st, sect);
}
/*
* PMT processing
*/
int
dvb_pmt_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
int r, sect, last, ver, had_components;
uint16_t sid;
mpegts_mux_t *mm = mt->mt_mux;
mpegts_service_t *s;
mpegts_table_state_t *st = NULL;
/* Start */
sid = ptr[0] << 8 | ptr[1];
r = dvb_table_begin(mt, ptr, len, tableid, sid, 9, &st, &sect, &last, &ver);
if (r != 1) return r;
/* Find service */
LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link)
if (s->s_dvb_service_id == sid) break;
if (!s) return -1;
/* Process */
tvhdebug("pmt", "sid %04X (%d)", sid, sid);
pthread_mutex_lock(&s->s_stream_mutex);
had_components = !!TAILQ_FIRST(&s->s_components);
r = psi_parse_pmt(mt->mt_mux, s, ptr, len);
pthread_mutex_unlock(&s->s_stream_mutex);
if (r)
service_restart((service_t*)s, had_components);
/* Finish */
return dvb_table_end(mt, st, sect);
}
/*
* NIT/BAT processing (because its near identical)
*/
static void
dvb_bat_destroy_lists( mpegts_table_t *mt )
{
dvb_bat_t *b = mt->mt_bat;
dvb_bat_id_t *bi;
dvb_bat_svc_t *bs;
dvb_freesat_region_t *fr;
dvb_freesat_svc_t *fs;
while ((bi = LIST_FIRST(&b->bats)) != NULL) {
while ((bs = TAILQ_FIRST(&bi->services)) != NULL) {
TAILQ_REMOVE(&bi->services, bs, link);
free(bs);
}
while ((fs = TAILQ_FIRST(&bi->fservices)) != NULL) {
TAILQ_REMOVE(&bi->fservices, fs, link);
free(fs);
}
while ((fr = LIST_FIRST(&bi->fregions)) != NULL) {
LIST_REMOVE(fr, link);
free(fr);
}
LIST_REMOVE(bi, link);
free(bi);
}
}
void
dvb_bat_destroy( mpegts_table_t *mt )
{
dvb_bat_destroy_lists(mt);
free(mt->mt_bat);
mt->mt_bat = NULL;
}
static void
dvb_bat_completed
( dvb_bat_t *b, const char *dstr, int tableid, int tsid, int nbid,
mpegts_mux_t *mux, bouquet_t *bq_alt )
{
dvb_bat_id_t *bi;
dvb_bat_svc_t *bs;
uint32_t services_count;
bouquet_t *bq;
b->complete = 1;
LIST_FOREACH(bi, &b->bats, link) {
if (bi->nbid != nbid) {
if (!bi->complete)
b->complete = 0;
continue;
}
if (bi->freesat || bi->bskyb) {
#if ENABLE_MPEGTS_DVB
dvb_freesat_completed(b, bi, dstr);
#endif
goto complete;
}
bq = NULL;
#if ENABLE_MPEGTS_DVB
if (tableid == 0x4A /* BAT */) {
char src[64] = "";
if (idnode_is_instance(&mux->mm_id, &dvb_mux_dvbs_class)) {
dvb_mux_conf_t *mc = &((dvb_mux_t *)mux)->lm_tuning;
if (mc->u.dmc_fe_qpsk.orbital_dir) {
char buf[16];
dvb_sat_position_to_str(dvb_sat_position(mc), buf, sizeof(buf));
snprintf(src, sizeof(src), "dvb-bouquet://dvbs,%s,%04X,%04X", buf, tsid, bi->nbid);
}
} else if (idnode_is_instance(&mux->mm_id, &dvb_mux_dvbt_class)) {
snprintf(src, sizeof(src), "dvb-bouquet://dvbt,%04X,%04X", tsid, bi->nbid);
} else if (idnode_is_instance(&mux->mm_id, &dvb_mux_dvbc_class)) {
snprintf(src, sizeof(src), "dvb-bouquet://dvbc,%04X,%04X", tsid, bi->nbid);
}
if (src[0])
bq = bouquet_find_by_source(bi->name, src, !TAILQ_EMPTY(&bi->services));
} else if (tableid == DVB_FASTSCAN_NIT_BASE) {
bq = bq_alt;
}
#endif
if (!bq) continue;
dvb_bouquet_comment(bq, bi->mm);
services_count = 0;
TAILQ_FOREACH(bs, &bi->services, link) {
services_count++;
if (bq->bq_enabled)
bouquet_add_service(bq, (service_t *)bs->svc,
(int64_t)bs->lcn * CHANNEL_SPLIT, 0);
}
bouquet_completed(bq, services_count);
complete:
bi->complete = 1;
}
}
int
dvb_nit_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
int save = 0;
int r, sect, last, ver;
uint32_t priv = 0;
uint8_t dtag;
int llen, dllen, dlen;
const uint8_t *lptr, *dlptr, *dptr;
uint16_t nbid = 0, onid, tsid;
mpegts_mux_t *mm = mt->mt_mux, *mux;
mpegts_network_t *mn = mm->mm_network;
char name[256], dauth[256];
mpegts_table_state_t *st = NULL;
bouquet_t *bq = NULL;
dvb_bat_t *b = NULL;
dvb_bat_id_t *bi = NULL;
const char *charset;
/* Net/Bat ID */
nbid = (ptr[0] << 8) | ptr[1];
/* Begin */
if (tableid != 0x40 && tableid != 0x41 && tableid != 0x4A &&
tableid != DVB_FASTSCAN_NIT_BASE)
return -1;
r = dvb_table_begin(mt, ptr, len, tableid, nbid, 7, &st, &sect, &last, &ver);
if (r == 0) {
if (tableid == 0x4A || tableid == DVB_FASTSCAN_NIT_BASE) {
if ((b = mt->mt_bat) != NULL) {
if (!b->complete) {
dvb_bat_completed(b, mt->mt_name, tableid, mm->mm_tsid, nbid,
mm, mt->mt_opaque);
mt->mt_working -= st->working;
st->working = 0;
}
if (b->complete)
dvb_bat_destroy_lists(mt);
}
}
}
if (r != 1) return r;
/* NIT */
if (tableid != 0x4A && tableid != DVB_FASTSCAN_NIT_BASE) {
/* Specific NID */
if (mn->mn_nid) {
if (mn->mn_nid != nbid) {
return dvb_table_end(mt, st, sect);
}
/* Only use "this" network */
} else if (tableid != 0x40) {
return dvb_table_end(mt, st, sect);
}
}
/* BAT ID lookup */
if (dvb_bouquets_parse &&
(tableid == 0x4A || tableid == DVB_FASTSCAN_NIT_BASE)) {
if ((b = mt->mt_bat) == NULL) {
b = calloc(1, sizeof(*b));
mt->mt_bat = b;
}
LIST_FOREACH(bi, &b->bats, link)
if (bi->nbid == nbid)
break;
if (!bi) {
bi = calloc(1, sizeof(*bi));
bi->nbid = nbid;
TAILQ_INIT(&bi->services);
TAILQ_INIT(&bi->fservices);
LIST_INSERT_HEAD(&b->bats, bi, link);
bi->mm = mm;
}
if (!st->working) {
st->working = 1;
mt->mt_working++;
mt->mt_flags |= MT_FASTSWITCH;
}
}
/* Network Descriptors */
*name = 0;
charset = dvb_charset_find(mn, NULL, NULL);
DVB_DESC_FOREACH(ptr, len, 5, lptr, llen, dtag, dlen, dptr) {
tvhtrace(mt->mt_name, " dtag %02X dlen %d", dtag, dlen);
switch (dtag) {
case DVB_DESC_BOUQUET_NAME:
case DVB_DESC_NETWORK_NAME:
if (dvb_get_string(name, sizeof(name), dptr, dlen, charset, NULL))
return -1;
break;
case DVB_DESC_MULTI_NETWORK_NAME:
// TODO: implement this?
break;
case DVB_DESC_PRIVATE_DATA:
#if ENABLE_MPEGTS_DVB
if (tableid == 0x4A && dlen == 4) {
priv = (dptr[0] << 24) | (dptr[1] << 16) | (dptr[2] << 8) | dptr[3];
tvhtrace(mt->mt_name, " private %08X", priv);
}
#endif
break;
case DVB_DESC_FREESAT_REGIONS:
#if ENABLE_MPEGTS_DVB
if (priv == PRIV_FSAT)
dvb_freesat_regions(bi, mt->mt_name, dptr, dlen);
#endif
break;
}
}
/* Fastscan */
if (tableid == DVB_FASTSCAN_NIT_BASE) {
tvhdebug(mt->mt_name, "fastscan %04X (%d) [%s]", nbid, nbid, name);
bq = mt->mt_opaque;
dvb_bouquet_comment(bq, mm);
/* BAT */
} else if (tableid == 0x4A) {
tvhdebug(mt->mt_name, "bouquet %04X (%d) [%s]", nbid, nbid, name);
if (bi && *name) {
strncpy(bi->name, name, sizeof(bi->name)-1);
bi->name[sizeof(bi->name)-1] = '\0';
}
/* NIT */
} else {
tvhdebug(mt->mt_name, "network %04X (%d) [%s]", nbid, nbid, name);
save |= mpegts_network_set_network_name(mn, name);
if (save)
mn->mn_config_save(mn);
}
/* Transport length */
priv = 0;
DVB_LOOP_FOREACH(ptr, len, 0, lptr, llen, 6) {
tsid = (lptr[0] << 8) | lptr[1];
onid = (lptr[2] << 8) | lptr[3];
/* Find existing mux */
LIST_FOREACH(mux, &mn->mn_muxes, mm_network_link)
if (mux->mm_onid == onid && mux->mm_tsid == tsid)
break;
charset = dvb_charset_find(mn, mux, NULL);
tvhdebug(mt->mt_name, " onid %04X (%d) tsid %04X (%d) mux %p", onid, onid, tsid, tsid, mux);
DVB_DESC_FOREACH(lptr, llen, 4, dlptr, dllen, dtag, dlen, dptr) {
tvhtrace(mt->mt_name, " dtag %02X dlen %d", dtag, dlen);
/* User-defined */
if (mt->mt_mux_cb && mux) {
int i = 0;
while (mt->mt_mux_cb[i].cb) {
if (mt->mt_mux_cb[i].tag == dtag)
break;
i++;
}
if (mt->mt_mux_cb[i].cb) {
if (mt->mt_mux_cb[i].cb(mt, mux, dtag, dptr, dlen))
return -1;
dtag = 0;
}
}
/* Pre-defined */
switch (dtag) {
default:
case 0:
break;
/* nit only */
#if ENABLE_MPEGTS_DVB
case DVB_DESC_SAT_DEL:
case DVB_DESC_CABLE_DEL:
case DVB_DESC_TERR_DEL:
if (dtag == DVB_DESC_SAT_DEL)
mux = dvb_desc_sat_del(mm, onid, tsid, dptr, dlen);
else if (dtag == DVB_DESC_CABLE_DEL)
mux = dvb_desc_cable_del(mm, onid, tsid, dptr, dlen);
else
mux = dvb_desc_terr_del(mm, onid, tsid, dptr, dlen);
if (mux) {
mpegts_mux_set_onid(mux, onid);
mpegts_mux_set_tsid(mux, tsid, 0);
}
break;
#endif
/* Both */
case DVB_DESC_DEF_AUTHORITY:
if (dvb_get_string(dauth, sizeof(dauth), dptr, dlen, charset, NULL))
return -1;
tvhdebug(mt->mt_name, " default auth [%s]", dauth);
if (mux && *dauth)
mpegts_mux_set_crid_authority(mux, dauth);
break;
case DVB_DESC_SERVICE_LIST:
if (dvb_desc_service_list(mt->mt_name, dptr, dlen, mux, bi))
return -1;
break;
case DVB_DESC_PRIVATE_DATA:
#if ENABLE_MPEGTS_DVB
if (dlen == 4) {
priv = (dptr[0] << 24) | (dptr[1] << 16) | (dptr[2] << 8) | dptr[3];
tvhtrace(mt->mt_name, " private %08X", priv);
}
#endif
break;
case 0x81:
if (priv == 0) goto lcn;
case 0x82:
if (priv == 0) goto lcn;
case 0x83:
if (priv == 0 || priv == 0x28 || priv == 0xa5) goto lcn;
case 0x86:
if (priv == 0) goto lcn;
case 0x93:
if (priv == 0 || priv == 0x362275)
/* fall thru */
lcn:
if (dvb_desc_local_channel(mt->mt_name, dptr, dlen, dtag, mux, bi))
return -1;
break;
case DVB_DESC_FREESAT_LCN:
#if ENABLE_MPEGTS_DVB
if (bi && tableid == 0x4A && priv == PRIV_FSAT) {
dvb_freesat_local_channels(bi, mt->mt_name, dptr, dlen);
bi->freesat = 1;
}
#endif
break;
case DVB_DESC_BSKYB_LCN:
#if ENABLE_MPEGTS_DVB
if (bi && tableid == 0x4A && priv == 2) {
dvb_bskyb_local_channels(bi, mt->mt_name, dptr, dlen, mux);
bi->bskyb = 1;
}
#endif
break;
}
}
}
/* End */
return dvb_table_end(mt, st, sect);
}
/**
* DVB SDT (Service Description Table)
*/
int
dvb_sdt_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
int r, sect, last, ver, extraid, p02 = 0;
uint16_t onid, tsid;
uint8_t dtag;
int llen, dlen;
const uint8_t *lptr, *dptr;
mpegts_mux_t *mm = mt->mt_mux;
mpegts_network_t *mn = mm->mm_network;
mpegts_table_state_t *st = NULL;
/* Begin */
tsid = ptr[0] << 8 | ptr[1];
onid = ptr[5] << 8 | ptr[6];
extraid = ((int)onid) << 16 | tsid;
if (tableid != 0x42 && tableid != 0x46) return -1;
r = dvb_table_begin(mt, ptr, len, tableid, extraid, 8, &st, &sect, &last, &ver);
if (r != 1) return r;
/* ID */
tvhdebug("sdt", "onid %04X (%d) tsid %04X (%d)", onid, onid, tsid, tsid);
/* Find Transport Stream */
if (tableid == 0x42) {
mpegts_mux_set_onid(mm, onid);
mpegts_mux_set_tsid(mm, tsid, 1);
} else {
LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
if (mm->mm_onid == onid && mm->mm_tsid == tsid)
break;
goto done;
}
/* Service loop */
len -= 8;
ptr += 8;
while(len >= 5) {
mpegts_service_t *s;
int master = 0, save = 0, save2 = 0;
uint16_t service_id = ptr[0] << 8 | ptr[1];
int free_ca_mode = (ptr[3] >> 4) & 0x1;
int stype = 0;
char sprov[256], sname[256], sauth[256];
int running_status = (ptr[3] >> 5) & 0x7;
const char *charset;
*sprov = *sname = *sauth = 0;
tvhdebug("sdt", " sid %04X (%d) running %d free_ca %d",
service_id, service_id, running_status, free_ca_mode);
/* Initialise the loop */
DVB_LOOP_INIT(ptr, len, 3, lptr, llen);
/* Find service */
s = mpegts_service_find(mm, service_id, 0, 1, &save);
charset = dvb_charset_find(mn, mm, s);
/* Descriptor loop */
DVB_DESC_EACH(lptr, llen, dtag, dlen, dptr) {
tvhtrace("sdt", " dtag %02X dlen %d", dtag, dlen);
switch (dtag) {
case DVB_DESC_SERVICE:
if (dvb_desc_service(dptr, dlen, &stype, sprov,
sizeof(sprov), sname, sizeof(sname), charset))
return -1;
break;
case DVB_DESC_DEF_AUTHORITY:
if (dvb_get_string(sauth, sizeof(sauth), dptr, dlen, charset, NULL))
return -1;
break;
case DVB_DESC_PRIVATE_DATA:
if (!memcmp(dptr, "\x00\x00\x00\x02", 4))
p02 = 1;
break;
case DVB_DESC_BSKYB_NVOD:
if (p02)
if (dvb_get_string(sname, sizeof(sname), dptr, dlen, charset, NULL))
return -1;
break;
}
}
tvhtrace("sdt", " type %02X (%d) name [%s] provider [%s] def_auth [%s]",
stype, stype, sname, sprov, sauth);
if (!s) continue;
/* Update service type */
if (stype && s->s_dvb_servicetype != stype) {
int r;
s->s_dvb_servicetype = stype;
save = 1;
tvhtrace("sdt", " type changed / old %02X (%i)",
s->s_dvb_servicetype, s->s_dvb_servicetype);
/* Set tvh service type */
if ((r = dvb_servicetype_lookup(stype)) != -1)
s->s_servicetype = r;
}
/* Check if this is master
* Some networks appear to provide diff service names on diff transponders
*/
if (tableid == 0x42)
master = 1;
/* Update CRID authority */
if (*sauth && strcmp(s->s_dvb_cridauth ?: "", sauth)) {
tvh_str_update(&s->s_dvb_cridauth, sauth);
save = 1;
tvhtrace("sdt", " cridauth changed");
}
/* Update name */
if (*sname && strcmp(s->s_dvb_svcname ?: "", sname)) {
if (!s->s_dvb_svcname || master) {
tvh_str_update(&s->s_dvb_svcname, sname);
save2 = 1;
tvhtrace("sdt", " name changed");
}
}
/* Update provider */
if (*sprov && strcmp(s->s_dvb_provider ?: "", sprov)) {
if (!s->s_dvb_provider || master) {
tvh_str_update(&s->s_dvb_provider, sprov);
save2 = 1;
tvhtrace("sdt", " provider changed");
}
}
/* Update nice name */
if (save2) {
pthread_mutex_lock(&s->s_stream_mutex);
service_make_nicename((service_t*)s);
pthread_mutex_unlock(&s->s_stream_mutex);
tvhdebug("sdt", " nicename %s", s->s_nicename);
save = 1;
}
/* Save details */
if (save) {
idnode_changed(&s->s_id);
service_refresh_channel((service_t*)s);
}
}
/* Done */
done:
return dvb_table_end(mt, st, sect);
}
/*
* ATSC VCT processing
*/
/* Done */
int
atsc_vct_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
int i, r, sect, last, ver, extraid, save, dlen;
int maj, min, count;
uint16_t tsid, sid, type;
char chname[256];
mpegts_mux_t *mm = mt->mt_mux;
mpegts_network_t *mn = mm->mm_network;
mpegts_service_t *s;
mpegts_table_state_t *st = NULL;
/* Validate */
if (tableid != 0xc8 && tableid != 0xc9) return -1;
/* Extra ID */
tsid = ptr[0] << 8 | ptr[1];
extraid = tsid;
/* Begin */
r = dvb_table_begin(mt, ptr, len, tableid, extraid, 7,
&st, &sect, &last, &ver);
if (r != 1) return r;
tvhdebug("vct", "tsid %04X (%d)", tsid, tsid);
/* # channels */
count = ptr[6];
tvhdebug("vct", "channel count %d", count);
ptr += 7;
len -= 7;
for (i = 0; i < count && len >= 32; i++) {
dlen = ((ptr[30] & 0x3) << 8) | ptr[31];
if (dlen + 32 > len) return -1;
/* Extract fields */
atsc_utf16_to_utf8(ptr, 7, chname, sizeof(chname));
maj = (ptr[14] & 0xF) << 6 | ptr[15] >> 2;
min = (ptr[15] & 0x3) << 2 | ptr[16];
tsid = (ptr[22]) << 8 | ptr[23];
sid = (ptr[24]) << 8 | ptr[25];
type = ptr[27] & 0x3f;
tvhdebug("vct", "tsid %04X (%d)", tsid, tsid);
tvhdebug("vct", "sid %04X (%d)", sid, sid);
tvhdebug("vct", "chname %s", chname);
tvhdebug("vct", "chnum %d.%d", maj, min);
tvhdebug("vct", "type %02X (%d)", type, type);
/* Skip */
if (type > 3)
goto next;
/* Find mux */
LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
if (mm->mm_tsid == tsid)
break;
if (!mm) goto next;
/* Find the service */
if (!(s = mpegts_service_find(mm, sid, 0, 1, &save)))
goto next;
/* Update */
if (strcmp(s->s_dvb_svcname ?: "", chname)) {
tvh_str_set(&s->s_dvb_svcname, chname);
save = 1;
}
if (s->s_dvb_channel_num != maj || s->s_dvb_channel_minor != min) {
s->s_dvb_channel_num = maj;
s->s_dvb_channel_minor = min;
save = 1;
}
/* Save */
if (save)
s->s_config_save((service_t*)s);
/* Move on */
next:
ptr += dlen + 32;
len -= dlen + 32;
}
return dvb_table_end(mt, st, sect);
}
/*
* DVB BAT processing
*/
int
dvb_bat_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
return dvb_nit_callback(mt, ptr, len, tableid);
}
#if ENABLE_MPEGTS_DVB
/*
* DVB fastscan table processing
*/
int
dvb_fs_sdt_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
int r, sect, last, ver;
uint8_t dtag;
int llen, dlen;
const uint8_t *lptr, *dptr;
uint16_t nbid = 0, onid, tsid, service_id;
mpegts_mux_t *mm = mt->mt_mux, *mux;
mpegts_network_t *mn = mm->mm_network;
mpegts_table_state_t *st = NULL;
/* Fastscan ID */
nbid = (ptr[0] << 8) | ptr[1];
/* Begin */
if (tableid != 0xBD)
return -1;
r = dvb_table_begin(mt, ptr, len, tableid, nbid, 7, &st, &sect, &last, &ver);
if (r == 0) {
mt->mt_working -= st->working;
st->working = 0;
}
if (r != 1) return r;
if (len < 5) return -1;
ptr += 5;
len -= 5;
while (len > 0) {
const char *charset;
char sprov[256] = "", sname[256] = "";
mpegts_service_t *s;
int stype = 0, save = 0;
onid = (ptr[0] << 8) | ptr[1];
tsid = (ptr[2] << 8) | ptr[3];
service_id = (ptr[4] << 8) | ptr[5];
/* (ptr[6] << 8) | ptr[7] - video pid */
/* (ptr[7] << 8) | ptr[8] - audio pid */
/* (ptr[9] << 8) | ptr[10] - video ecm pid */
/* (ptr[10] << 8) | ptr[12] - audio ecm pid */
/* (ptr[14] << 8) | ptr[15] - pcr pid */
tvhdebug(mt->mt_name, " service %04X (%d) onid %04X (%d) tsid %04X (%d)",
service_id, service_id, onid, onid, tsid, tsid);
/* Initialise the loop */
DVB_LOOP_INIT(ptr, len, 16, lptr, llen);
/* Find existing mux */
LIST_FOREACH(mux, &mn->mn_muxes, mm_network_link)
if (mux->mm_onid == onid && mux->mm_tsid == tsid)
break;
if (!mux) {
tvhtrace(mt->mt_name, " mux not found");
continue;
}
/* Find service */
s = mpegts_service_find(mux, service_id, 0, 1, &save);
charset = dvb_charset_find(mn, mux, s);
/* Descriptor loop */
DVB_DESC_EACH(lptr, llen, dtag, dlen, dptr) {
tvhtrace(mt->mt_name, " dtag %02X dlen %d", dtag, dlen);
switch (dtag) {
case DVB_DESC_SERVICE:
if (dvb_desc_service(dptr, dlen, &stype, sprov,
sizeof(sprov), sname, sizeof(sname), charset))
return -1;
break;
case DVB_DESC_SAT_DEL:
mux = dvb_desc_sat_del(mm, onid, tsid, dptr, dlen);
if (mux) {
mpegts_mux_set_onid(mux, onid);
mpegts_mux_set_tsid(mux, tsid, 0);
}
break;
}
}
tvhtrace(mt->mt_name, " type %d name [%s] provider [%s]",
stype, sname, sprov);
/* Update service type */
if (stype && !s->s_dvb_servicetype) {
int r;
s->s_dvb_servicetype = stype;
save = 1;
tvhtrace(mt->mt_name, " type changed");
/* Set tvh service type */
if ((r = dvb_servicetype_lookup(stype)) != -1)
s->s_servicetype = r;
}
/* Update name */
if (*sname && strcmp(s->s_dvb_svcname ?: "", sname)) {
if (!s->s_dvb_svcname) {
tvh_str_update(&s->s_dvb_svcname, sname);
save = 1;
tvhtrace(mt->mt_name, " name changed");
}
}
/* Update provider */
if (*sprov && strcmp(s->s_dvb_provider ?: "", sprov)) {
if (!s->s_dvb_provider) {
tvh_str_update(&s->s_dvb_provider, sprov);
save = 1;
tvhtrace(mt->mt_name, " provider changed");
}
}
if (save) {
/* Update nice name */
pthread_mutex_lock(&s->s_stream_mutex);
service_make_nicename((service_t*)s);
pthread_mutex_unlock(&s->s_stream_mutex);
tvhdebug(mt->mt_name, " nicename %s", s->s_nicename);
/* Save changes */
idnode_changed(&s->s_id);
service_refresh_channel((service_t*)s);
}
if (!st->working) {
st->working = 1;
mt->mt_working++;
mt->mt_flags |= MT_FASTSWITCH;
}
}
/* End */
return dvb_table_end(mt, st, sect);
}
#endif
/**
* PMT update reason flags
*/
#define PMT_UPDATE_PCR 0x1
#define PMT_UPDATE_NEW_STREAM 0x2
#define PMT_UPDATE_LANGUAGE 0x4
#define PMT_UPDATE_AUDIO_TYPE 0x8
#define PMT_UPDATE_FRAME_DURATION 0x10
#define PMT_UPDATE_COMPOSITION_ID 0x20
#define PMT_UPDATE_ANCILLARY_ID 0x40
#define PMT_UPDATE_STREAM_DELETED 0x80
#define PMT_UPDATE_NEW_CA_STREAM 0x100
#define PMT_UPDATE_NEW_CAID 0x200
#define PMT_UPDATE_CA_PROVIDER_CHANGE 0x400
#define PMT_UPDATE_PARENT_PID 0x800
#define PMT_UPDATE_CAID_DELETED 0x1000
#define PMT_REORDERED 0x2000
/**
* Add a CA descriptor
*/
static int
psi_desc_add_ca
(mpegts_service_t *t, uint16_t caid, uint32_t provid, uint16_t pid)
{
elementary_stream_t *st;
caid_t *c;
int r = 0;
tvhdebug("pmt", " caid %04X (%s) provider %08X pid %04X",
caid, descrambler_caid2name(caid), provid, pid);
if((st = service_stream_find((service_t*)t, pid)) == NULL) {
st = service_stream_create((service_t*)t, pid, SCT_CA);
r |= PMT_UPDATE_NEW_CA_STREAM;
}
st->es_delete_me = 0;
st->es_position = 0x40000;
LIST_FOREACH(c, &st->es_caids, link) {
if(c->caid == caid) {
c->pid = pid;
if(c->providerid != provid) {
c->providerid = provid;
r |= PMT_UPDATE_CA_PROVIDER_CHANGE;
}
return r;
}
}
c = malloc(sizeof(caid_t));
c->caid = caid;
c->providerid = provid;
c->use = 1;
c->pid = pid;
LIST_INSERT_HEAD(&st->es_caids, c, link);
r |= PMT_UPDATE_NEW_CAID;
return r;
}
/**
* Parser for CA descriptors
*/
static int
psi_desc_ca(mpegts_service_t *t, const uint8_t *buffer, int size)
{
int r = 0;
int i;
uint32_t provid = 0;
uint16_t caid = (buffer[0] << 8) | buffer[1];
uint16_t pid = ((buffer[2]&0x1F) << 8) | buffer[3];
switch (caid & 0xFF00) {
case 0x0100: // SECA/Mediaguard
provid = (buffer[4] << 8) | buffer[5];
//Add extra providers, if any
for (i = 17; i < size; i += 15){
uint16_t xpid = ((buffer[i]&0x1F) << 8) | buffer[i + 1];
uint16_t xprovid = (buffer[i + 2] << 8) | buffer[i + 3];
r |= psi_desc_add_ca(t, caid, xprovid, xpid);
}
break;
case 0x0500:// Viaccess
for (i = 4; i < size;) {
unsigned char nano = buffer[i++];
unsigned char nanolen = buffer[i++];
if (nano == 0x14) {
provid = (buffer[i] << 16) | (buffer[i + 1] << 8) | (buffer[i + 2] & 0xf0);
break;
}
i += nanolen;
}
break;
case 0x4a00:
if (caid == 0x4ad2)//streamguard
provid=0;
if (caid != 0x4aee && caid != 0x4ad2) { // Bulcrypt
provid = size < 4 ? 0 : buffer[4];
}
break;
default:
provid = 0;
break;
}
r |= psi_desc_add_ca(t, caid, provid, pid);
return r;
}
/**
* Parser for teletext descriptor
*/
static int
psi_desc_teletext(mpegts_service_t *t, const uint8_t *ptr, int size,
int parent_pid, int *position)
{
int r = 0;
const char *lang;
elementary_stream_t *st;
while(size >= 5) {
int page = (ptr[3] & 0x7 ?: 8) * 100 + (ptr[4] >> 4) * 10 + (ptr[4] & 0xf);
int type = ptr[3] >> 3;
if(type == 2 || type == 5) {
// 2 = subtitle page, 5 = subtitle page [hearing impaired]
// We put the teletext subtitle driven streams on a list of pids
// higher than normal MPEG TS (0x2000 ++)
int pid = DVB_TELETEXT_BASE + page;
if((st = service_stream_find((service_t*)t, pid)) == NULL) {
r |= PMT_UPDATE_NEW_STREAM;
st = service_stream_create((service_t*)t, pid, SCT_TEXTSUB);
st->es_delete_me = 1;
}
lang = lang_code_get2((const char*)ptr, 3);
if(memcmp(st->es_lang,lang,3)) {
r |= PMT_UPDATE_LANGUAGE;
memcpy(st->es_lang, lang, 4);
}
if(st->es_parent_pid != parent_pid) {
r |= PMT_UPDATE_PARENT_PID;
st->es_parent_pid = parent_pid;
}
// Check es_delete_me so we only compute position once per PMT update
if(st->es_position != *position && st->es_delete_me) {
st->es_position = *position;
r |= PMT_REORDERED;
}
st->es_delete_me = 0;
(*position)++;
}
ptr += 5;
size -= 5;
}
return r;
}
/**
* PMT parser, from ISO 13818-1 and ETSI EN 300 468
*/
static int
psi_parse_pmt
(mpegts_mux_t *mux, mpegts_service_t *t, const uint8_t *ptr, int len)
{
int ret = 0;
uint16_t pcr_pid, pid;
uint8_t estype;
int dllen;
uint8_t dtag, dlen;
streaming_component_type_t hts_stream_type;
elementary_stream_t *st, *next;
int update = 0;
int composition_id;
int ancillary_id;
int version;
int position;
int tt_position;
const char *lang;
uint8_t audio_type;
caid_t *c, *cn;
lock_assert(&t->s_stream_mutex);
version = ptr[2] >> 1 & 0x1f;
pcr_pid = (ptr[5] & 0x1f) << 8 | ptr[6];
dllen = (ptr[7] & 0xf) << 8 | ptr[8];
if(t->s_pcr_pid != pcr_pid) {
t->s_pcr_pid = pcr_pid;
update |= PMT_UPDATE_PCR;
}
tvhdebug("pmt", " pcr_pid %04X", pcr_pid);
ptr += 9;
len -= 9;
/* Mark all streams for deletion */
TAILQ_FOREACH(st, &t->s_components, es_link) {
st->es_delete_me = 1;
LIST_FOREACH(c, &st->es_caids, link)
c->pid = CAID_REMOVE_ME;
}
// Common descriptors
while(dllen > 1) {
dtag = ptr[0];
dlen = ptr[1];
tvhlog_hexdump("pmt", ptr, dlen + 2);
len -= 2; ptr += 2; dllen -= 2;
if(dlen > len)
break;
switch(dtag) {
case DVB_DESC_CA:
update |= psi_desc_ca(t, ptr, dlen);
break;
default:
break;
}
len -= dlen; ptr += dlen; dllen -= dlen;
}
while(len >= 5) {
estype = ptr[0];
pid = (ptr[1] & 0x1f) << 8 | ptr[2];
dllen = (ptr[3] & 0xf) << 8 | ptr[4];
tvhdebug("pmt", " pid %04X estype %d", pid, estype);
tvhlog_hexdump("pmt", ptr, 5);
ptr += 5;
len -= 5;
hts_stream_type = SCT_UNKNOWN;
composition_id = -1;
ancillary_id = -1;
position = 0;
tt_position = 1000;
lang = NULL;
audio_type = 0;
switch(estype) {
case 0x01:
case 0x02:
case 0x80: // 0x80 is DigiCipher II (North American cable) encrypted MPEG-2
hts_stream_type = SCT_MPEG2VIDEO;
break;
case 0x03:
case 0x04:
hts_stream_type = SCT_MPEG2AUDIO;
break;
case 0x06:
/* 0x06 is Chinese Cable TV AC-3 audio track */
/* but mark it so only when no more descriptors exist */
if (dllen > 1 || !mux || !mux->mm_pmt_06_ac3)
break;
/* fall through to SCT_AC3 */
case 0x81:
hts_stream_type = SCT_AC3;
break;
case 0x0f:
hts_stream_type = SCT_MP4A;
break;
case 0x11:
hts_stream_type = SCT_AAC;
break;
case 0x1b:
hts_stream_type = SCT_H264;
break;
case 0x24:
hts_stream_type = SCT_HEVC;
break;
default:
break;
}
while(dllen > 1) {
dtag = ptr[0];
dlen = ptr[1];
tvhlog_hexdump("pmt", ptr, dlen + 2);
len -= 2; ptr += 2; dllen -= 2;
if(dlen > len)
break;
switch(dtag) {
case DVB_DESC_CA:
update |= psi_desc_ca(t, ptr, dlen);
break;
case DVB_DESC_REGISTRATION:
if(dlen == 4 &&
ptr[0] == 'A' && ptr[1] == 'C' && ptr[2] == '-' && ptr[3] == '3')
hts_stream_type = SCT_AC3;
/* seen also these formats: */
/* LU-A, ADV1 */
break;
case DVB_DESC_LANGUAGE:
lang = lang_code_get2((const char*)ptr, 3);
audio_type = ptr[3];
break;
case DVB_DESC_TELETEXT:
if(estype == 0x06)
hts_stream_type = SCT_TELETEXT;
update |= psi_desc_teletext(t, ptr, dlen, pid, &tt_position);
break;
case DVB_DESC_AC3:
if(estype == 0x06 || estype == 0x81)
hts_stream_type = SCT_AC3;
break;
case DVB_DESC_AAC:
if(estype == 0x0f)
hts_stream_type = SCT_MP4A;
else if(estype == 0x11)
hts_stream_type = SCT_AAC;
break;
case DVB_DESC_SUBTITLE:
if(dlen < 8)
break;
lang = lang_code_get2((const char*)ptr, 3);
composition_id = ptr[4] << 8 | ptr[5];
ancillary_id = ptr[6] << 8 | ptr[7];
hts_stream_type = SCT_DVBSUB;
break;
case DVB_DESC_EAC3:
if(estype == 0x06 || estype == 0x81)
hts_stream_type = SCT_EAC3;
break;
default:
break;
}
len -= dlen; ptr += dlen; dllen -= dlen;
}
if(hts_stream_type != SCT_UNKNOWN) {
if((st = service_stream_find((service_t*)t, pid)) == NULL) {
update |= PMT_UPDATE_NEW_STREAM;
st = service_stream_create((service_t*)t, pid, hts_stream_type);
}
st->es_type = hts_stream_type;
st->es_delete_me = 0;
tvhdebug("pmt", " type %s position %d",
streaming_component_type2txt(st->es_type), position);
if (lang)
tvhdebug("pmt", " language %s", lang);
if (composition_id != -1)
tvhdebug("pmt", " composition_id %d", composition_id);
if (ancillary_id != -1)
tvhdebug("pmt", " ancillary_id %d", ancillary_id);
if(st->es_position != position) {
update |= PMT_REORDERED;
st->es_position = position;
}
if(lang && memcmp(st->es_lang, lang, 3)) {
update |= PMT_UPDATE_LANGUAGE;
memcpy(st->es_lang, lang, 4);
}
if(st->es_audio_type != audio_type) {
update |= PMT_UPDATE_AUDIO_TYPE;
st->es_audio_type = audio_type;
}
if(composition_id != -1 && st->es_composition_id != composition_id) {
st->es_composition_id = composition_id;
update |= PMT_UPDATE_COMPOSITION_ID;
}
if(ancillary_id != -1 && st->es_ancillary_id != ancillary_id) {
st->es_ancillary_id = ancillary_id;
update |= PMT_UPDATE_ANCILLARY_ID;
}
}
position++;
}
/* Scan again to see if any streams should be deleted */
for(st = TAILQ_FIRST(&t->s_components); st != NULL; st = next) {
next = TAILQ_NEXT(st, es_link);
for(c = LIST_FIRST(&st->es_caids); c != NULL; c = cn) {
cn = LIST_NEXT(c, link);
if (c->pid == CAID_REMOVE_ME) {
LIST_REMOVE(c, link);
free(c);
update |= PMT_UPDATE_CAID_DELETED;
}
}
if(st->es_delete_me) {
service_stream_destroy((service_t*)t, st);
update |= PMT_UPDATE_STREAM_DELETED;
}
}
if(update & PMT_REORDERED)
sort_elementary_streams((service_t*)t);
if(update) {
tvhdebug("pmt", "Service \"%s\" PMT (version %d) updated"
"%s%s%s%s%s%s%s%s%s%s%s%s%s",
service_nicename((service_t*)t), version,
update&PMT_UPDATE_PCR ? ", PCR PID changed":"",
update&PMT_UPDATE_NEW_STREAM ? ", New elementary stream":"",
update&PMT_UPDATE_LANGUAGE ? ", Language changed":"",
update&PMT_UPDATE_FRAME_DURATION ? ", Frame duration changed":"",
update&PMT_UPDATE_COMPOSITION_ID ? ", Composition ID changed":"",
update&PMT_UPDATE_ANCILLARY_ID ? ", Ancillary ID changed":"",
update&PMT_UPDATE_STREAM_DELETED ? ", Stream deleted":"",
update&PMT_UPDATE_NEW_CA_STREAM ? ", New CA stream":"",
update&PMT_UPDATE_NEW_CAID ? ", New CAID":"",
update&PMT_UPDATE_CA_PROVIDER_CHANGE? ", CA provider changed":"",
update&PMT_UPDATE_PARENT_PID ? ", Parent PID changed":"",
update&PMT_UPDATE_CAID_DELETED ? ", CAID deleted":"",
update&PMT_REORDERED ? ", PIDs reordered":"");
service_request_save((service_t*)t, 0);
// Only restart if something that our clients worry about did change
if(update & ~(PMT_UPDATE_NEW_CA_STREAM |
PMT_UPDATE_NEW_CAID |
PMT_UPDATE_CA_PROVIDER_CHANGE |
PMT_UPDATE_CAID_DELETED)) {
if(t->s_status == SERVICE_RUNNING)
ret = 1;
}
// notify descrambler that we found another CAIDs
if (update & PMT_UPDATE_NEW_CAID)
descrambler_caid_changed((service_t *)t);
}
return ret;
}
/*
* Install default table sets
*/
void
psi_tables_default ( mpegts_mux_t *mm )
{
mpegts_table_add(mm, DVB_PAT_BASE, DVB_PAT_MASK, dvb_pat_callback,
NULL, "pat", MT_QUICKREQ | MT_CRC | MT_RECORD,
DVB_PAT_PID);
mpegts_table_add(mm, DVB_CAT_BASE, DVB_CAT_MASK, dvb_cat_callback,
NULL, "cat", MT_QUICKREQ | MT_CRC, DVB_CAT_PID);
}
#if ENABLE_MPEGTS_DVB
static void
psi_tables_dvb_fastscan( void *aux, bouquet_t *bq, const char *name, int pid )
{
tvhtrace("fastscan", "adding table %04X (%i) for '%s'", pid, pid, name);
mpegts_table_add(aux, DVB_FASTSCAN_NIT_BASE, DVB_FASTSCAN_MASK,
dvb_nit_callback, bq, "fs_nit", MT_CRC, pid);
mpegts_table_add(aux, DVB_FASTSCAN_SDT_BASE, DVB_FASTSCAN_MASK,
dvb_fs_sdt_callback, NULL, "fs_sdt", MT_CRC, pid);
}
#endif
void
psi_tables_dvb ( mpegts_mux_t *mm )
{
mpegts_table_add(mm, DVB_NIT_BASE, DVB_NIT_MASK, dvb_nit_callback,
NULL, "nit", MT_QUICKREQ | MT_CRC, DVB_NIT_PID);
mpegts_table_add(mm, DVB_SDT_BASE, DVB_SDT_MASK, dvb_sdt_callback,
NULL, "sdt", MT_QUICKREQ | MT_CRC | MT_RECORD,
DVB_SDT_PID);
mpegts_table_add(mm, DVB_BAT_BASE, DVB_BAT_MASK, dvb_bat_callback,
NULL, "bat", MT_CRC, DVB_BAT_PID);
#if ENABLE_MPEGTS_DVB
if (idnode_is_instance(&mm->mm_id, &dvb_mux_dvbs_class)) {
dvb_mux_conf_t *mc = &((dvb_mux_t *)mm)->lm_tuning;
if (mc->dmc_fe_type == DVB_TYPE_S)
dvb_fastscan_each(mm, dvb_sat_position(mc),
mc->dmc_fe_freq, psi_tables_dvb_fastscan);
}
#endif
}
void
psi_tables_atsc_c ( mpegts_mux_t *mm )
{
mpegts_table_add(mm, DVB_VCT_C_BASE, DVB_VCT_MASK, atsc_vct_callback,
NULL, "vct", MT_QUICKREQ | MT_CRC | MT_RECORD,
DVB_VCT_PID);
}
void
psi_tables_atsc_t ( mpegts_mux_t *mm )
{
mpegts_table_add(mm, DVB_VCT_T_BASE, DVB_VCT_MASK, atsc_vct_callback,
NULL, "vct", MT_QUICKREQ | MT_CRC | MT_RECORD,
DVB_VCT_PID);
}