tvheadend/src/dvb/dvb_tables.c
Andreas Öman be1a42d909 * Try to detect duplicate EPG entries from the DVB feed and adjust
EPG accordingly. The EPG code will search for events with the same
    DVB event ID +- 2 events from the current one. If the event id is
    equal, the prvious (old) entry will be removed in favor of the new one.
    Reason for not blindingly trusting the event id is that some networks
    seem to (incorrectly) reuse IDs.
    Ticket #65
2009-07-18 20:07:47 +00:00

1093 lines
22 KiB
C

/*
* DVB Table support
* Copyright (C) 2007 Andreas Ö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 <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include "tvhead.h"
#include "dvb.h"
#include "dvb_support.h"
#include "epg.h"
#include "transports.h"
#include "channels.h"
#include "psi.h"
#include "notify.h"
#define TDT_CRC 0x1
#define TDT_QUICKREQ 0x2
#define TDT_INC_TABLE_HDR 0x4
static int tdt_id_tally;
/**
*
*/
typedef struct th_dvb_table {
/**
* Flags, must never be changed after creation.
* We inspect it without holding global_lock
*/
int tdt_flags;
/**
* Cycle queue
* Tables that did not get a fd or filter in hardware will end up here
* waiting for any other table to be received so it can reuse that fd.
* Only linked if fd == -1
*/
TAILQ_ENTRY(th_dvb_table) tdt_pending_link;
/**
* File descriptor for filter
*/
int tdt_fd;
LIST_ENTRY(th_dvb_table) tdt_link;
char *tdt_name;
void *tdt_opaque;
int (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
uint8_t tableid, void *opaque);
int tdt_count;
int tdt_pid;
struct dmx_sct_filter_params *tdt_fparams;
int tdt_id;
} th_dvb_table_t;
/**
* Helper for preparing a section filter parameter struct
*/
static struct dmx_sct_filter_params *
dvb_fparams_alloc(void)
{
return calloc(1, sizeof(struct dmx_sct_filter_params));
}
/**
*
*/
static void
dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi)
{
th_dvb_table_t *tdt;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
char buf[100];
if(!tdmi->tdmi_table_initial)
return;
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link)
if((tdt->tdt_flags & TDT_QUICKREQ) && tdt->tdt_count == 0)
return;
tdmi->tdmi_table_initial = 0;
tda->tda_initial_num_mux--;
if(tda->tda_logging) {
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tvhlog(LOG_INFO, "dvb", "\"%s\" initial scan completed for \"%s\"",
tda->tda_rootpath, buf);
}
dvb_adapter_mux_scanner(tda);
}
/**
*
*/
static void
tdt_open_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
struct epoll_event e;
assert(tdt->tdt_fd == -1);
TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
tdt->tdt_fd = open(tda->tda_demux_path, O_RDWR);
if(tdt->tdt_fd != -1) {
tdt->tdt_id = ++tdt_id_tally;
e.events = EPOLLIN;
e.data.u64 = ((uint64_t)tdt->tdt_fd << 32) | tdt->tdt_id;
if(epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_ADD, tdt->tdt_fd, &e)) {
close(tdt->tdt_fd);
tdt->tdt_fd = -1;
} else {
if(ioctl(tdt->tdt_fd, DMX_SET_FILTER, tdt->tdt_fparams)) {
close(tdt->tdt_fd);
tdt->tdt_fd = -1;
}
}
}
if(tdt->tdt_fd == -1)
TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
}
/**
* Close FD for the given table and put table on the pending list
*/
static void
tdt_close_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
assert(tdt->tdt_fd != -1);
epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL);
close(tdt->tdt_fd);
tdt->tdt_fd = -1;
TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
}
/**
*
*/
static void
dvb_proc_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt, uint8_t *sec,
int r)
{
int chkcrc = tdt->tdt_flags & TDT_CRC;
int tableid, len;
uint8_t *ptr;
int ret;
/* It seems some hardware (or is it the dvb API?) does not
honour the DMX_CHECK_CRC flag, so we check it again */
if(chkcrc && psi_crc32(sec, r))
return;
r -= 3;
tableid = sec[0];
len = ((sec[1] & 0x0f) << 8) | sec[2];
if(len < r)
return;
ptr = &sec[3];
if(chkcrc) len -= 4; /* Strip trailing CRC */
if(tdt->tdt_flags & TDT_INC_TABLE_HDR)
ret = tdt->tdt_callback(tdmi, sec, len + 3, tableid, tdt->tdt_opaque);
else
ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque);
if(ret == 0)
tdt->tdt_count++;
if(tdt->tdt_flags & TDT_QUICKREQ)
dvb_table_fastswitch(tdmi);
}
/**
*
*/
static void *
dvb_table_input(void *aux)
{
th_dvb_adapter_t *tda = aux;
int r, i, tid, fd, x;
struct epoll_event ev[1];
uint8_t sec[4096];
th_dvb_mux_instance_t *tdmi;
th_dvb_table_t *tdt;
int64_t t;
while(1) {
x = epoll_wait(tda->tda_table_epollfd, ev, sizeof(ev) / sizeof(ev[0]), -1);
for(i = 0; i < x; i++) {
tid = ev[i].data.u64 & 0xffffffff;
fd = ev[i].data.u64 >> 32;
if(!(ev[i].events & EPOLLIN))
continue;
if((r = read(fd, sec, sizeof(sec))) < 3)
continue;
pthread_mutex_lock(&global_lock);
if((tdmi = tda->tda_mux_current) != NULL) {
t = getclock_hires();
/*
* Supress first 250ms of table info. It seems that sometimes
* the tuners not have actually tuned once they have returned
* from the ioctl(). So we will wait some time before we start
* accepting tables.
* Not a perfect tix...
*/
if(t - tdmi->tdmi_table_start >= 250000) {
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link)
if(tdt->tdt_id == tid)
break;
if(tdt != NULL) {
dvb_proc_table(tdmi, tdt, sec, r);
/* Any tables pending (that wants a filter/fd) */
if(TAILQ_FIRST(&tdmi->tdmi_table_queue) != NULL) {
tdt_close_fd(tdmi, tdt);
tdt = TAILQ_FIRST(&tdmi->tdmi_table_queue);
assert(tdt != NULL);
tdt_open_fd(tdmi, tdt);
}
}
}
}
pthread_mutex_unlock(&global_lock);
}
}
return NULL;
}
/**
*
*/
void
dvb_table_init(th_dvb_adapter_t *tda)
{
pthread_t ptid;
tda->tda_table_epollfd = epoll_create(50);
pthread_create(&ptid, NULL, dvb_table_input, tda);
}
/**
*
*/
static void
dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi,
th_dvb_table_t *tdt)
{
LIST_REMOVE(tdt, tdt_link);
if(tdt->tdt_fd == -1) {
TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
} else {
epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL);
close(tdt->tdt_fd);
}
free(tdt->tdt_name);
free(tdt->tdt_fparams);
free(tdt);
}
/**
* Add a new DVB table
*/
static void
tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams,
int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
uint8_t tableid, void *opaque), void *opaque,
const char *name, int flags, int pid, th_dvb_table_t *tdt)
{
th_dvb_table_t *t;
LIST_FOREACH(t, &tdmi->tdmi_tables, tdt_link) {
if(pid == t->tdt_pid) {
free(tdt);
free(fparams);
return;
}
}
if(fparams == NULL)
fparams = dvb_fparams_alloc();
if(flags & TDT_CRC) fparams->flags |= DMX_CHECK_CRC;
fparams->flags |= DMX_IMMEDIATE_START;
fparams->pid = pid;
if(tdt == NULL)
tdt = calloc(1, sizeof(th_dvb_table_t));
tdt->tdt_name = strdup(name);
tdt->tdt_callback = callback;
tdt->tdt_opaque = opaque;
tdt->tdt_pid = pid;
tdt->tdt_flags = flags;
tdt->tdt_fparams = fparams;
LIST_INSERT_HEAD(&tdmi->tdmi_tables, tdt, tdt_link);
tdt->tdt_fd = -1;
TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
tdt_open_fd(tdmi, tdt);
}
/**
* DVB Descriptor; Short Event
*/
static int
dvb_desc_short_event(uint8_t *ptr, int len,
char *title, size_t titlelen,
char *desc, size_t desclen)
{
int r;
if(len < 4)
return -1;
ptr += 3; len -= 3;
if((r = dvb_get_string_with_len(title, titlelen, ptr, len)) < 0)
return -1;
ptr += r; len -= r;
if((r = dvb_get_string_with_len(desc, desclen, ptr, len)) < 0)
return -1;
return 0;
}
/**
* DVB Descriptor; Service
*/
static int
dvb_desc_service(uint8_t *ptr, int len, uint8_t *typep,
char *provider, size_t providerlen,
char *name, size_t namelen)
{
int r;
if(len < 2)
return -1;
*typep = ptr[0];
ptr++;
len--;
if((r = dvb_get_string_with_len(provider, providerlen, ptr, len)) < 0)
return -1;
ptr += r; len -= r;
if((r = dvb_get_string_with_len(name, namelen, ptr, len)) < 0)
return -1;
ptr += r; len -= r;
return 0;
}
/**
* DVB EIT (Event Information Table)
*/
static int
dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
th_transport_t *t;
channel_t *ch;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
uint16_t serviceid;
int version;
uint8_t section_number;
uint8_t last_section_number;
uint16_t transport_stream_id;
uint16_t original_network_id;
uint8_t segment_last_section_number;
uint8_t last_table_id;
uint16_t event_id;
time_t start_time, stop_time;
int duration;
int dllen;
uint8_t dtag, dlen;
char title[256];
char desc[5000];
epg_content_type_t *ect;
event_t *e;
lock_assert(&global_lock);
// printf("EIT!, tid = %x\n", tableid);
if(tableid < 0x4e || tableid > 0x6f || len < 11)
return -1;
serviceid = ptr[0] << 8 | ptr[1];
version = ptr[2] >> 1 & 0x1f;
section_number = ptr[3];
last_section_number = ptr[4];
transport_stream_id = ptr[5] << 8 | ptr[6];
original_network_id = ptr[7] << 8 | ptr[8];
segment_last_section_number = ptr[9];
last_table_id = ptr[10];
if((ptr[2] & 1) == 0) {
/* current_next_indicator == next, skip this */
return -1;
}
len -= 11;
ptr += 11;
/* Search all muxes on adapter */
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link)
if(tdmi->tdmi_transport_stream_id == transport_stream_id)
break;
if(tdmi == NULL)
return -1;
t = dvb_transport_find(tdmi, serviceid, 0, NULL);
if(t == NULL)
return 0;
ch = t->tht_ch;
if(ch == NULL)
return 0;
while(len >= 12) {
event_id = ptr[0] << 8 | ptr[1];
start_time = dvb_convert_date(&ptr[2]);
duration = bcdtoint(ptr[7] & 0xff) * 3600 +
bcdtoint(ptr[8] & 0xff) * 60 +
bcdtoint(ptr[9] & 0xff);
dllen = ((ptr[10] & 0x0f) << 8) | ptr[11];
len -= 12;
ptr += 12;
if(dllen > len)
break;
stop_time = start_time + duration;
if(stop_time < dispatch_clock) {
/* Already come to pass, skip over it */
len -= dllen;
ptr += dllen;
continue;
}
if((e = epg_event_create(ch, start_time, start_time + duration,
event_id)) == NULL) {
len -= dllen;
ptr += dllen;
continue;
}
ect = NULL;
*title = 0;
*desc = 0;
while(dllen > 0) {
dtag = ptr[0];
dlen = ptr[1];
len -= 2; ptr += 2; dllen -= 2;
if(dlen > len)
break;
switch(dtag) {
case DVB_DESC_SHORT_EVENT:
if(!dvb_desc_short_event(ptr, dlen,
title, sizeof(title),
desc, sizeof(desc))) {
epg_event_set_title(e, title);
epg_event_set_desc(e, desc);
}
break;
case DVB_DESC_CONTENT:
if(dlen >= 2) {
/* We only support one content type per event atm. */
ect = epg_content_type_find_by_dvbcode(*ptr);
epg_event_set_content_type(e, ect);
}
break;
}
len -= dlen; ptr += dlen; dllen -= dlen;
}
}
return 0;
}
/**
* DVB SDT (Service Description Table)
*/
static int
dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
th_transport_t *t;
int version;
uint8_t section_number;
uint8_t last_section_number;
uint16_t service_id;
uint16_t transport_stream_id;
uint16_t original_network_id;
int reserved;
int running_status, free_ca_mode;
int dllen;
uint8_t dtag, dlen;
char provider[256];
char chname0[256], *chname;
uint8_t stype;
int l;
if(len < 8)
return -1;
transport_stream_id = ptr[0] << 8 | ptr[1];
version = ptr[2] >> 1 & 0x1f;
section_number = ptr[3];
last_section_number = ptr[4];
original_network_id = ptr[5] << 8 | ptr[6];
reserved = ptr[7];
if((ptr[2] & 1) == 0) {
/* current_next_indicator == next, skip this */
return -1;
}
len -= 8;
ptr += 8;
while(len >= 5) {
service_id = ptr[0] << 8 | ptr[1];
reserved = ptr[2];
running_status = (ptr[3] >> 5) & 0x7;
free_ca_mode = (ptr[3] >> 4) & 0x1;
dllen = ((ptr[3] & 0x0f) << 8) | ptr[4];
len -= 5;
ptr += 5;
if(dllen > len)
break;
stype = 0;
chname = NULL;
while(dllen > 2) {
dtag = ptr[0];
dlen = ptr[1];
len -= 2; ptr += 2; dllen -= 2;
if(dlen > len)
break;
switch(dtag) {
case DVB_DESC_SERVICE:
if(dvb_desc_service(ptr, dlen, &stype,
provider, sizeof(provider),
chname0, sizeof(chname0)) == 0) {
chname = chname0;
/* Some providers insert spaces.
Clean up that (both heading and trailing) */
while(*chname <= 32 && *chname != 0)
chname++;
l = strlen(chname);
while(l > 1 && chname[l - 1] <= 32) {
chname[l - 1] = 0;
l--;
}
if(l == 0) {
chname = chname0;
snprintf(chname0, sizeof(chname0), "noname-sid-0x%x", service_id);
}
t = dvb_transport_find(tdmi, service_id, 0, NULL);
if(t == NULL)
break;
if(t->tht_servicetype != stype ||
t->tht_scrambled != free_ca_mode ||
strcmp(t->tht_provider ?: "", provider) ||
strcmp(t->tht_svcname ?: "", chname )) {
t->tht_servicetype = stype;
t->tht_scrambled = free_ca_mode;
free((void *)t->tht_provider);
t->tht_provider = strdup(provider);
free((void *)t->tht_svcname);
t->tht_svcname = strdup(chname);
pthread_mutex_lock(&t->tht_stream_mutex);
t->tht_config_change(t);
pthread_mutex_unlock(&t->tht_stream_mutex);
}
}
break;
}
len -= dlen; ptr += dlen; dllen -= dlen;
}
}
return 0;
}
/**
* PAT - Program Allocation table
*/
static int
dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
uint16_t service, pmt, tsid;
th_transport_t *t;
if(len < 5)
return -1;
if((ptr[2] & 1) == 0) {
/* current_next_indicator == next, skip this */
return -1;
}
tsid = (ptr[0] << 8) | ptr[1];
if(tdmi->tdmi_transport_stream_id != tsid)
dvb_mux_set_tsid(tdmi, tsid);
ptr += 5;
len -= 5;
while(len >= 4) {
service = ptr[0] << 8 | ptr[1];
pmt = (ptr[2] & 0x1f) << 8 | ptr[3];
if(service != 0) {
t = dvb_transport_find(tdmi, service, pmt, NULL);
dvb_table_add_transport(tdmi, t, pmt);
}
ptr += 4;
len -= 4;
}
return 0;
}
typedef struct ca_stream {
th_dvb_table_t tdt;
int cs_caid;
} ca_stream_t;
/**
* CA - Conditional Access
*/
static int
dvb_ca_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
return 0;
}
/**
* CAT - Conditional Access Table
*/
static int
dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
int tag, tlen;
uint16_t caid;
uint16_t pid;
ca_stream_t *cs;
if((ptr[2] & 1) == 0) {
/* current_next_indicator == next, skip this */
return -1;
}
ptr += 5;
len -= 5;
while(len > 2) {
tag = *ptr++;
tlen = *ptr++;
len -= 2;
switch(tag) {
case DVB_DESC_CA:
caid = (ptr[0] << 8) | ptr[1];
pid = ((ptr[2] & 0x1f << 8)) | ptr[3];
if(pid == 0)
break;
cs = calloc(1, sizeof(ca_stream_t));
cs->cs_caid = caid;
tdt_add(tdmi, NULL, dvb_ca_callback, cs, "CA",
TDT_INC_TABLE_HDR, pid, &cs->tdt);
break;
default:
break;
}
ptr += tlen;
len -= tlen;
}
return 0;
}
/**
* Tables for delivery descriptor parsing
*/
static const fe_code_rate_t fec_tab [8] = {
FEC_AUTO, FEC_1_2, FEC_2_3, FEC_3_4,
FEC_5_6, FEC_7_8, FEC_NONE, FEC_NONE
};
static const fe_modulation_t qam_tab [6] = {
QAM_AUTO, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256
};
/**
* Cable delivery descriptor
*/
static int
dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint16_t tsid)
{
int freq, symrate;
struct dvb_frontend_parameters fe_param;
if(!tdmi->tdmi_adapter->tda_autodiscovery)
return -1;
if(len < 11) {
printf("Invalid CABLE DESCRIPTOR\n");
return -1;
}
memset(&fe_param, 0, sizeof(fe_param));
fe_param.inversion = INVERSION_AUTO;
freq =
bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 +
bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3]);
fe_param.frequency = freq * 100;
symrate =
bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 +
bcdtoint(ptr[9]) * 10 + (ptr[10] >> 4);
fe_param.u.qam.symbol_rate = symrate * 100;
if((ptr[6] & 0x0f) > 5)
fe_param.u.qam.modulation = QAM_AUTO;
else
fe_param.u.qam.modulation = qam_tab[ptr[6] & 0x0f];
fe_param.u.qam.fec_inner = fec_tab[ptr[10] & 0x07];
dvb_mux_create(tdmi->tdmi_adapter, &fe_param, 0, 0, tsid, NULL,
"automatic mux discovery", 1, NULL);
return 0;
}
/**
* Satellite delivery descriptor
*/
static int
dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint16_t tsid)
{
int freq, symrate, pol;
struct dvb_frontend_parameters fe_param;
if(!tdmi->tdmi_adapter->tda_autodiscovery)
return -1;
if(len < 11)
return -1;
memset(&fe_param, 0, sizeof(fe_param));
fe_param.inversion = INVERSION_AUTO;
freq =
bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 +
bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3]);
fe_param.frequency = freq * 10;
symrate =
bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 +
bcdtoint(ptr[9]) * 10 + (ptr[10] >> 4);
fe_param.u.qam.symbol_rate = symrate * 100;
fe_param.u.qam.fec_inner = fec_tab[ptr[10] & 0x07];
pol = (ptr[6] >> 5) & 0x03;
dvb_mux_create(tdmi->tdmi_adapter, &fe_param, pol, tdmi->tdmi_satconf,
tsid, NULL,
"automatic mux discovery", 1, NULL);
return 0;
}
/**
* NIT - Network Information Table
*/
static int
dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
uint8_t tag, tlen;
int ntl;
char networkname[256];
uint16_t tsid;
if(tableid != 0x40)
return -1;
if((ptr[2] & 1) == 0) {
/* current_next_indicator == next, skip this */
return -1;
}
ptr += 5;
len -= 5;
ntl = ((ptr[0] & 0xf) << 8) | ptr[1];
ptr += 2;
len -= 2;
if(ntl > len)
return -1;
while(ntl > 2) {
tag = *ptr++;
tlen = *ptr++;
len -= 2;
ntl -= 2;
switch(tag) {
case DVB_DESC_NETWORK_NAME:
if(dvb_get_string(networkname, sizeof(networkname), ptr, tlen))
return -1;
if(strcmp(tdmi->tdmi_network ?: "", networkname))
dvb_mux_set_networkname(tdmi, networkname);
break;
}
ptr += tlen;
len -= tlen;
ntl -= tlen;
}
if(len < 2)
return -1;
ntl = ((ptr[0] & 0xf) << 8) | ptr[1];
ptr += 2;
len -= 2;
if(len < ntl)
return -1;
while(len >= 6) {
tsid = ( ptr[0] << 8) | ptr[1];
ntl = ((ptr[4] & 0xf) << 8) | ptr[5];
ptr += 6;
len -= 6;
if(ntl > len)
break;
while(ntl > 2) {
tag = *ptr++;
tlen = *ptr++;
len -= 2;
ntl -= 2;
switch(tag) {
case DVB_DESC_SAT:
dvb_table_sat_delivery(tdmi, ptr, tlen, tsid);
break;
case DVB_DESC_CABLE:
dvb_table_cable_delivery(tdmi, ptr, tlen, tsid);
break;
}
ptr += tlen;
len -= tlen;
ntl -= tlen;
}
}
return 0;
}
/**
* PMT - Program Mapping Table
*/
static int
dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
th_transport_t *t = opaque;
pthread_mutex_lock(&t->tht_stream_mutex);
psi_parse_pmt(t, ptr, len, 1);
pthread_mutex_unlock(&t->tht_stream_mutex);
return 0;
}
/**
* Setup FD + demux for default DVB tables that we want
*/
void
dvb_table_add_default(th_dvb_mux_instance_t *tdmi)
{
struct dmx_sct_filter_params *fp;
tdmi->tdmi_table_start = getclock_hires();
/* Program Allocation Table */
fp = dvb_fparams_alloc();
fp->filter.filter[0] = 0x00;
fp->filter.mask[0] = 0xff;
tdt_add(tdmi, fp, dvb_pat_callback, NULL, "pat",
TDT_QUICKREQ | TDT_CRC, 0, NULL);
/* Conditional Access Table */
fp = dvb_fparams_alloc();
fp->filter.filter[0] = 0x1;
fp->filter.mask[0] = 0xff;
tdt_add(tdmi, fp, dvb_cat_callback, NULL, "cat",
TDT_CRC, 1, NULL);
/* Network Information Table */
fp = dvb_fparams_alloc();
fp->filter.filter[0] = 0x40;
fp->filter.mask[0] = 0xff;
tdt_add(tdmi, fp, dvb_nit_callback, NULL, "nit",
TDT_QUICKREQ | TDT_CRC, 0x10, NULL);
/* Service Descriptor Table */
fp = dvb_fparams_alloc();
fp->filter.filter[0] = 0x42;
fp->filter.mask[0] = 0xff;
tdt_add(tdmi, fp, dvb_sdt_callback, NULL, "sdt",
TDT_QUICKREQ | TDT_CRC, 0x11, NULL);
/* Event Information table */
fp = dvb_fparams_alloc();
tdt_add(tdmi, fp, dvb_eit_callback, NULL, "eit",
TDT_CRC, 0x12, NULL);
}
/**
* Setup FD + demux for a services PMT
*/
void
dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t,
int pmt_pid)
{
struct dmx_sct_filter_params *fp;
char pmtname[100];
snprintf(pmtname, sizeof(pmtname), "PMT(%d), service:%d",
pmt_pid, t->tht_dvb_service_id);
fp = dvb_fparams_alloc();
fp->filter.filter[0] = 0x02;
fp->filter.mask[0] = 0xff;
tdt_add(tdmi, fp, dvb_pmt_callback, t, pmtname,
TDT_CRC | TDT_QUICKREQ, pmt_pid, NULL);
}
/**
*
*/
void
dvb_table_flush_all(th_dvb_mux_instance_t *tdmi)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
th_dvb_table_t *tdt;
while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL)
dvb_tdt_destroy(tda, tdmi, tdt);
}