mpegts: lots more work on PSI processing.

I think most tables are now processed and are making the relevant calls
into the mpegts system to update/create entities.
This commit is contained in:
Adam Sutton 2013-05-10 14:24:05 +01:00
parent ce6851e589
commit 080aafbf2c
6 changed files with 387 additions and 66 deletions

View file

@ -169,6 +169,7 @@ struct mpegts_network
(mpegts_mux_t*, uint16_t onid, uint16_t tsid, dvb_mux_conf_t *conf);
mpegts_service_t* (*mn_create_service)
(mpegts_mux_t*, uint16_t sid, uint16_t pmt_pid);
void (*mn_config_save) (mpegts_network_t*);
// Note: the above are slightly odd in that they take mux instead of
// network as initial param. This is intentional as we need to
@ -245,6 +246,10 @@ struct mpegts_mux
void (*mm_open_table) (mpegts_mux_t*,mpegts_table_t*);
void (*mm_close_table) (mpegts_mux_t*,mpegts_table_t*);
/*
* Fields
*/
char *mm_dvb_default_authority;
#if 0
dvb_mux_conf_t dm_conf;
@ -444,6 +449,9 @@ void mpegts_network_schedule_initial_scan
void mpegts_network_add_input ( mpegts_network_t *mn, mpegts_input_t *mi );
int mpegts_network_set_nid ( mpegts_network_t *mn, uint16_t nid );
int mpegts_network_set_network_name ( mpegts_network_t *mn, const char *name );
mpegts_mux_t *mpegts_mux_create0
( mpegts_mux_t *mm, const idclass_t *class, const char *uuid,
mpegts_network_t *mn, uint16_t onid, uint16_t tsid );
@ -469,9 +477,9 @@ mpegts_mux_instance_t *mpegts_mux_instance_create0
&type##_class, uuid,\
mi, mm);
void mpegts_mux_set_tsid ( mpegts_mux_t *mm, uint16_t tsid, int force );
void mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid, int force );
int mpegts_mux_set_tsid ( mpegts_mux_t *mm, uint16_t tsid, int force );
int mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid, int force );
int mpegts_mux_set_default_authority ( mpegts_mux_t *mm, const char *defauth );
size_t mpegts_input_recv_packets
(mpegts_input_t *mi, mpegts_mux_instance_t *mmi, uint8_t *tsb, size_t len,

View file

@ -30,6 +30,12 @@ struct mpegts_table;
/* Defaults */
/* PIDs */
#define DVB_PAT_PID 0x00
#define DVB_NIT_PID 0x10
#define DVB_SDT_PID 0x11
#define DVB_BAT_PID 0x11
/* Tables */
@ -63,6 +69,7 @@ struct mpegts_table;
#define DVB_DESC_SERVICE_LIST 0x41
#define DVB_DESC_SAT_DEL 0x43
#define DVB_DESC_CABLE_DEL 0x44
#define DVB_DESC_BOUQUET_NAME 0x47
#define DVB_DESC_SHORT_EVENT 0x4D
#define DVB_DESC_EXT_EVENT 0x4E
#define DVB_DESC_SERVICE 0x48
@ -109,23 +116,34 @@ void atsc_utf16_to_utf8(uint8_t *src, int len, char *buf, int buflen);
* PSI processing
*/
#define FOREACH_DVB_LOOP0(ptr,len,off,min,inc,llen) \
for ( llen = (ptr[off] & 0xF) << 8 | ptr[off+1],\
ptr += off + 2,\
len -= off + 2;\
(llen > min);\
ptr += inc, llen -= inc + min )\
if (llen > len) return -1;\
#define DVB_LOOP_INIT(ptr, len, off, lptr, llen)\
do {\
llen = ((ptr[off] & 0xF) << 8) | ptr[off+1];\
lptr = 2 + off + ptr;\
ptr += 2 + off + llen;\
len -= 2 + off + llen;\
if (len < 0) {tvhtrace("psi", "len < 0"); return -1; }\
} while(0)
#define DVB_LOOP_EACH(ptr, len, min)\
for ( ; len > min ; )\
#define DVB_LOOP_FOREACH(ptr, len, off, lptr, llen, min)\
DVB_LOOP_INIT(ptr, len, off, lptr, llen);\
DVB_LOOP_EACH(lptr, llen, min)
#define DVB_DESC_EACH(ptr, len, dtag, dlen, dptr)\
DVB_LOOP_EACH(ptr, len, 2)\
if (!(dtag = ptr[0])) {tvhtrace("psi", "1");return -1;}\
else if ((dlen = ptr[1]) < 0) {tvhtrace("psi", "2");return -1;}\
else if (!(dptr = ptr+2)) {tvhtrace("psi", "3");return -1;}\
else if ( (len -= 2 + dlen) < 0) {tvhtrace("psi", "4");return -1;}\
else if (!(ptr += 2 + dlen)) {tvhtrace("psi", "5");return -1;}\
else
#define FOREACH_DVB_LOOP(ptr,len,off,min,llen)\
FOREACH_DVB_LOOP0(ptr,len,off,min,0,llen)
#define FOREACH_DVB_DESC(ptr,len,off,llen,dtag,dlen) \
FOREACH_DVB_LOOP0(ptr,len,off,2,dlen,llen)\
if (!(dtag = *ptr++)) return -1;\
else if ((dlen = *ptr++) > llen - 2) return -1;\
else
#define DVB_DESC_FOREACH(ptr, len, off, lptr, llen, dtag, dlen, dptr)\
DVB_LOOP_INIT(ptr, len, off, lptr, llen);\
DVB_DESC_EACH(lptr, llen, dtag, dlen, dptr)\
/* PSI descriptors */

View file

@ -272,6 +272,70 @@ dvb_desc_terr_del
#endif /* ENABLE_DVBAPI */
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 )
{
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, NULL, NULL)) < 0)
return -1;
/* Name */
if (dvb_get_string_with_len(sname, sname_len, ptr+r, len-r, NULL, NULL) < 0)
return -1;
/* Cleanup name */
str = sname;
while (*str && *str <= 32)
str++;
strncpy(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 int
dvb_desc_def_authority
( const uint8_t *ptr, int len,
char *sauth, size_t sauth_len )
{
if (dvb_get_string_with_len(sauth, sauth_len, ptr, len, NULL, NULL) < 0)
return -1;
return 0;
}
static int
dvb_desc_service_list
( const char *dstr, const uint8_t *ptr, int len, mpegts_mux_t *mm )
{
uint16_t stype, sid;
int i;
for (i = 0; i < len; i += 3) {
sid = (ptr[i] << 8) | ptr[i+1];
stype = ptr[i+2];
tvhtrace(dstr, " service %04X (%d) type %d", sid, sid, stype);
if (mm)
mm->mm_network->mn_create_service(mm, sid, 0);
}
return 0;
}
/* **************************************************************************
* Tables
* *************************************************************************/
@ -285,7 +349,7 @@ dvb_pat_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
uint16_t sid, pid, tsid;
uint16_t nit_pid = 0x10;
uint16_t nit_pid = DVB_NIT_PID;
mpegts_mux_t *mm = mt->mt_mux;
tvhtrace("pat", "tableid %02X len %d", tableid, len);
tvhlog_hexdump("pat", ptr, len);
@ -374,22 +438,24 @@ dvb_pmt_callback
}
/*
* NIT processing
* NIT/BAT processing (because its near identical)
*/
int
dvb_nit_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
int i;
uint8_t dlen, dtag, stype;
int save;
uint8_t dlen, dtag;
uint16_t llen, dllen;
uint16_t nid, onid, tsid, sid;
const uint8_t *lptr, *dlptr, *dptr;
uint16_t bid = 0, nid = 0, onid, tsid;
mpegts_mux_t *mm = mt->mt_mux, *mux;
mpegts_network_t *mn = mm->mm_network;
char name[256];
char name[256], dauth[256];
const char *dstr = (tableid == 0x4A) ? "bat" : "nit";
tvhtrace("nit", "tableid %02X len %d", tableid, len);
tvhlog_hexdump("nit", ptr, len);
tvhtrace(dstr, "tableid %02X len %d", tableid, len);
tvhlog_hexdump(dstr, ptr, len);
/* Not long enough */
if (len < 7)
@ -399,25 +465,34 @@ dvb_nit_callback
if (!(ptr[2] & 0x01))
return -1;
/* Specific NID */
nid = (ptr[0] << 8) | ptr[1];
if (mn->mn_nid) {
if (mn->mn_nid != nid)
return -1;
/* BAT */
if (tableid == 0x4A) {
bid = (ptr[0] << 8) | ptr[1];
/* NIT */
} else {
nid = (ptr[0] << 8) | ptr[1];
/* Specific NID */
if (mn->mn_nid) {
if (mn->mn_nid != nid)
return -1;
/* Only use "this" network */
} else if (tableid != 0x40) {
return -1;
/* Only use "this" network */
} else if (tableid != 0x40) {
return -1;
}
}
/* Network Descriptors */
*name = 0;
FOREACH_DVB_DESC(ptr, len, 5, llen, dtag, dlen) {
tvhtrace("nit", " dtag %02X dlen %d", dtag, dlen);
DVB_DESC_FOREACH(ptr, len, 5, lptr, llen, dtag, dlen, dptr) {
tvhtrace(dstr, " 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), ptr, dlen, NULL, NULL))
if (dvb_get_string(name, sizeof(name), dptr, dlen, NULL, NULL))
return -1;
break;
case DVB_DESC_MULTI_NETWORK_NAME:
@ -425,42 +500,60 @@ dvb_nit_callback
break;
}
}
tvhtrace("nit", "network %04X (%d) [%s]", nid, nid, name);
// TODO: set network name
/* BAT */
if (tableid == 0x4A) {
tvhtrace("bat", "bouquet %04X (%d) [%s]", bid, bid, name);
/* NIT */
} else {
tvhtrace("nit", "network %04X (%d) [%s]", nid, nid, name);
save = mpegts_network_set_nid(mn, nid);
save |= mpegts_network_set_network_name(mn, name);
if (save)
mn->mn_config_save(mn);
}
/* Transport length */
FOREACH_DVB_LOOP(ptr, len, 0, 6, llen) {
mux = NULL;
tsid = (ptr[0] << 8) | ptr[1];
onid = (ptr[2] << 8) | ptr[3];
tvhtrace("nit", " onid %04X (%d) tsid %04X (%d)", onid, onid, tsid, tsid);
DVB_LOOP_FOREACH(ptr, len, 0, lptr, llen, 6) {
tsid = (lptr[0] << 8) | lptr[1];
onid = (lptr[2] << 8) | lptr[3];
tvhtrace(dstr, " onid %04X (%d) tsid %04X (%d)", onid, onid, tsid, tsid);
FOREACH_DVB_DESC(ptr, len, 4, dllen, dtag, dlen) {
llen -= 2 + dlen;
tvhtrace("nit", " dtag %02X dlen %d", dtag, dlen);
//tvhlog_hexdump("nit", ptr, dlen);
/* Find existing mux */
LIST_FOREACH(mux, &mn->mn_muxes, mm_network_link)
if (mux->mm_onid == onid && mux->mm_tsid == tsid)
break;
DVB_DESC_FOREACH(lptr, llen, 4, dlptr, dllen, dtag, dlen, dptr) {
tvhtrace(dstr, " dtag %02X dlen %d", dtag, dlen);
switch (dtag) {
/* NIT only */
case DVB_DESC_SAT_DEL:
mux = dvb_desc_sat_del(mm, onid, tsid, ptr, dlen);
mux = dvb_desc_sat_del(mm, onid, tsid, dptr, dlen);
break;
case DVB_DESC_CABLE_DEL:
mux = dvb_desc_cable_del(mm, onid, tsid, ptr, dlen);
mux = dvb_desc_cable_del(mm, onid, tsid, dptr, dlen);
break;
case DVB_DESC_TERR_DEL:
mux = dvb_desc_terr_del(mm, onid, tsid, ptr, dlen);
mux = dvb_desc_terr_del(mm, onid, tsid, dptr, dlen);
break;
/* Both */
case DVB_DESC_DEF_AUTHORITY:
if (dvb_desc_def_authority(dptr, dlen, dauth, sizeof(dauth)))
return -1;
tvhtrace(dstr, " default auth [%s]", dauth);
if (mux && *dauth)
mpegts_mux_set_default_authority(mux, dauth);
break;
case DVB_DESC_LOCAL_CHAN:
break;
case DVB_DESC_SERVICE_LIST:
for (i = 0; i < dlen; i += 3) {
sid = (ptr[i] << 8) | ptr[i+1];
stype = ptr[i+2];
tvhtrace("nit", " service %04X (%d) type %d", sid, sid, stype);
if (mux)
mux->mm_network->mn_create_service(mux, sid, 0);
}
if (dvb_desc_service_list(dstr, dptr, dlen, mux))
return -1;
break;
}
}
@ -469,6 +562,164 @@ dvb_nit_callback
return 0;
}
/**
* DVB SDT (Service Description Table)
*/
int
dvb_sdt_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
uint16_t onid, tsid;
uint16_t llen;
uint8_t dtag, dlen;
const uint8_t *lptr, *dptr;
mpegts_mux_t *mm = mt->mt_mux;
tvhtrace("sdt", "tableid %02X len %d", tableid, len);
tvhlog_hexdump("sdt", ptr, len);
/* Not enough data */
if(len < 8)
return -1;
/* Ignore next */
if((ptr[2] & 1) == 0)
return -1;
/* Validate */
if (tableid != 0x42 && tableid != 0x46)
return -1;
/* ID */
tsid = ptr[0] << 8 | ptr[1];
onid = ptr[5] << 8 | ptr[6];
tvhtrace("sdt", "onid %04X (%d) tsid %04X (%d)", onid, onid, tsid, tsid);
/* Find Transport Stream */
if (tableid == 0x42) {
mpegts_mux_set_onid(mm, onid, 0);
mpegts_mux_set_tsid(mm, tsid, 0);
if (mm->mm_onid != onid || mm->mm_tsid != tsid)
return -1;
} else {
mpegts_network_t *mn = mm->mm_network;
LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
if (mm->mm_onid == onid && mm->mm_tsid == tsid)
break;
if (!mm) return -1;
}
/* 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];
#if ENABLE_TRACE
int running_status = (ptr[3] >> 5) & 0x7;
#endif
*sprov = *sname = *sauth = 0;
tvhtrace("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 */
if (!(s = mm->mm_network->mn_create_service(mm, service_id, 0)))
continue;
/* 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)))
return -1;
break;
case DVB_DESC_DEF_AUTHORITY:
if (dvb_desc_def_authority(dptr, dlen, sauth, sizeof(sauth)))
return -1;
break;
}
}
tvhtrace("sdt", " type %d name [%s] provider [%s] def_auth [%s]",
stype, sname, sprov, sauth);
/* Update service type */
if (stype && s->s_dvb_servicetype != stype) {
s->s_dvb_servicetype = stype;
save = 1;
}
/* Update scrambled state */
if (s->s_scrambled != free_ca_mode) {
s->s_scrambled = free_ca_mode;
save = 1;
}
/* 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_default_authority ?: "", sauth)) {
tvh_str_update(&s->s_dvb_default_authority, sauth);
save = 1;
}
/* 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;
}
}
/* 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;
}
}
/* 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);
tvhtrace("sdt", " nicename %s", s->s_nicename);
save = 1;
}
/* Save details */
if (save) {
s->s_config_save((service_t*)s);
service_refresh_channel((service_t*)s);
}
}
return 0;
}
/*
* 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);
}
/**
* PMT update reason flags
*/

View file

@ -60,22 +60,35 @@ const idclass_t mpegts_mux_class =
}
};
void mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid, int force )
int
mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid, int force )
{
if (onid == mm->mm_onid)
return;
return 0;
if (!force && mm->mm_onid != MPEGTS_ONID_NONE)
return;
return 0;
mm->mm_onid = onid;
return 1;
}
void mpegts_mux_set_tsid ( mpegts_mux_t *mm, uint16_t tsid, int force )
int
mpegts_mux_set_tsid ( mpegts_mux_t *mm, uint16_t tsid, int force )
{
if (tsid == mm->mm_tsid)
return;
return 0;
if (!force && mm->mm_tsid != MPEGTS_TSID_NONE)
return;
return 0;
mm->mm_tsid = tsid;
return 1;
}
int
mpegts_mux_set_default_authority ( mpegts_mux_t *mm, const char *defauth )
{
if (defauth && !strcmp(defauth, mm->mm_dvb_default_authority ?: ""))
return 0;
tvh_str_update(&mm->mm_dvb_default_authority, defauth);
return 1;
}
static void

View file

@ -28,6 +28,12 @@ const idclass_t mpegts_network_class =
}
};
static void
mpegts_network_config_save
( mpegts_network_t *mn )
{
}
static mpegts_mux_t *
mpegts_network_create_mux
( mpegts_mux_t *mm, uint16_t sid, uint16_t tsid, dvb_mux_conf_t *aux )
@ -70,12 +76,33 @@ mpegts_network_create0
{
idnode_insert(&mn->mn_id, uuid, idc);
mn->mn_create_mux = mpegts_network_create_mux;
mn->mn_config_save = mpegts_network_config_save;
mn->mn_network_name = strdup(netname);
TAILQ_INIT(&mn->mn_initial_scan_pending_queue);
TAILQ_INIT(&mn->mn_initial_scan_current_queue);
return mn;
}
int
mpegts_network_set_nid
( mpegts_network_t *mn, uint16_t nid )
{
if (mn->mn_nid == nid)
return 0;
mn->mn_nid = nid;
return 1;
}
int
mpegts_network_set_network_name
( mpegts_network_t *mn, const char *name )
{
if (!name || !strcmp(name, mn->mn_network_name))
return 0;
tvh_str_update(&mn->mn_network_name, name);
return 1;
}
/******************************************************************************
* Editor Configuration
*

View file

@ -214,7 +214,11 @@ tsfile_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *t )
/* Install table handlers */
mpegts_table_add(mm, DVB_PAT_BASE, DVB_PAT_MASK, dvb_pat_callback,
NULL, "pat", MT_QUICKREQ| MT_CRC, 0);
NULL, "pat", MT_QUICKREQ | MT_CRC, DVB_PAT_PID);
mpegts_table_add(mm, DVB_SDT_BASE, DVB_SDT_MASK, dvb_sdt_callback,
NULL, "sdt", MT_QUICKREQ | MT_CRC, DVB_SDT_PID);
mpegts_table_add(mm, DVB_BAT_BASE, DVB_BAT_MASK, dvb_bat_callback,
NULL, "bat", MT_CRC, DVB_BAT_PID);
#if 0
mpegts_table_add(mm, 0x1, 0xff, dvb_cat_callback, NULL, "cat",
MT_CRC, 1);