#include <linux/dvb/frontend.h>
#include <pthread.h>
#include "htsmsg.h"
+#include "psi.h"
struct service;
struct th_dvb_table;
LIST_HEAD(, th_dvb_table) tdmi_tables;
+ int tdmi_num_tables;
+
TAILQ_HEAD(, th_dvb_table) tdmi_table_queue;
int tdmi_table_initial;
} th_dvb_mux_instance_t;
+
+
+/**
+ * When in raw mode we need to enqueue raw TS packet
+ * to a different thread because we need to hold
+ * global_lock when doing delivery of the tables
+ */
+TAILQ_HEAD(dvb_table_feed_queue, dvb_table_feed);
+
+typedef struct dvb_table_feed {
+ TAILQ_ENTRY(dvb_table_feed) dtf_link;
+ uint8_t dtf_tsb[188];
+} dvb_table_feed_t;
+
+
+
/**
* DVB Adapter (one of these per physical adapter)
*/
int tda_rawmode;
+
+ struct dvb_table_feed_queue tda_table_feed;
+ pthread_cond_t tda_table_feed_cond; // Bound to tda_delivery_mutex
+
+ // PIDs that needs to be requeued and processed as tables
+ uint8_t tda_table_filter[8192];
+
+
} th_dvb_adapter_t;
/**
int tdt_fd;
LIST_ENTRY(th_dvb_table) tdt_link;
+ th_dvb_mux_instance_t *tdt_tdmi;
char *tdt_name;
int tdt_table;
int tdt_mask;
+ int tdt_destroyed;
+ int tdt_refcount;
+
+ psi_section_t tdt_sect; // Manual reassembly
} th_dvb_table_t;
void dvb_table_rem_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid);
-void dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi);
-
void tdt_add(th_dvb_mux_instance_t *tdmi, int table, int mask,
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);
+ const char *name, int flags, int pid);
int dvb_pidx11_callback
(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
#define TDT_CA 0x4
#define TDT_TDT 0x8
+void dvb_table_dispatch(uint8_t *sec, int r, th_dvb_table_t *tdt);
+
+void dvb_table_release(th_dvb_table_t *tdt);
+
/**
* Satellite configuration
*/
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
- dvb_input_filtered_setup(tda);
+ dvb_input_raw_setup(tda);
if(tda->tda_sat)
dvb_satconf_init(tda);
/* not enough data */
if (r < 188) continue;
+ int wakeup_table_feed = 0; // Just wanna wakeup once
+
pthread_mutex_lock(&tda->tda_delivery_mutex);
/* Process */
while (r >= 188) {
/* sync */
if (tsb[i] == 0x47) {
+
+ if(!(tsb[i+1] & 0x80)) { // Only dispatch to table parser if not error
+ int pid = (tsb[i+1] & 0x1f) << 8 | tsb[i+2];
+ if(tda->tda_table_filter[pid]) {
+ dvb_table_feed_t *dtf = malloc(sizeof(dvb_table_feed_t));
+ memcpy(dtf->dtf_tsb, tsb + i, 188);
+ TAILQ_INSERT_TAIL(&tda->tda_table_feed, dtf, dtf_link);
+ wakeup_table_feed = 1;
+ }
+ }
+
LIST_FOREACH(t, &tda->tda_transports, s_active_link)
if(t->s_dvb_mux_instance == tda->tda_mux_current)
ts_recv_packet1(t, tsb + i, NULL);
}
}
+ if(wakeup_table_feed)
+ pthread_cond_signal(&tda->tda_table_feed_cond);
+
pthread_mutex_unlock(&tda->tda_delivery_mutex);
/* reset buffer */
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+ lock_assert(&global_lock);
+
assert(tdmi == tda->tda_mux_current);
tda->tda_mux_current = NULL;
struct epoll_event e;
static int tdt_id_tally;
- assert(tdt->tdt_fd == -1);
- TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
-
tdt->tdt_fd = tvh_open(tda->tda_demux_path, O_RDWR, 0);
if(tdt->tdt_fd != -1) {
}
-/**
- *
- */
-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 && tvh_crc32(sec, r, 0xffffffff))
- 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_CA)
- ret = tdt->tdt_callback((th_dvb_mux_instance_t *)tdt,
- sec, len + 3, tableid, tdt->tdt_opaque);
- else if(tdt->tdt_flags & TDT_TDT)
- ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt);
- 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);
-}
/**
*
break;
if(tdt != NULL) {
- dvb_proc_table(tdmi, tdt, sec, r);
+ dvb_table_dispatch(sec, r, tdt);
/* Any tables pending (that wants a filter/fd), close this one */
if(TAILQ_FIRST(&tdmi->tdmi_table_queue) != NULL &&
cycle_barrier = getmonoclock() + 100000;
tdt = TAILQ_FIRST(&tdmi->tdmi_table_queue);
assert(tdt != NULL);
+ TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
open_table(tdmi, tdt);
}
static void
open_service(th_dvb_adapter_t *tda, service_t *s)
{
+ // NOP -- We receive all PIDs anyway
}
static void
close_service(th_dvb_adapter_t *tda, service_t *s)
{
+ // NOP -- open_service() is a NOP
}
static void
open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
{
+ th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+ assert(tdt->tdt_pid < 0x2000);
+ tda->tda_table_filter[tdt->tdt_pid] = 1;
}
static void
close_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
{
+ th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+ assert(tdt->tdt_pid < 0x2000);
+ tda->tda_table_filter[tdt->tdt_pid] = 0;
+}
+
+
+/**
+ *
+ */
+static void
+got_section(const uint8_t *data, size_t len, void *opaque)
+{
+ th_dvb_table_t *tdt = opaque;
+ dvb_table_dispatch((uint8_t *)data, len, tdt);
+}
+
+
+
+/**
+ * All the tables can be destroyed from any of the callbacks
+ * so we need to be a bit careful here
+ */
+static void
+dvb_table_raw_dispatch(th_dvb_mux_instance_t *tdmi,
+ const dvb_table_feed_t *dtf)
+{
+ int pid = (dtf->dtf_tsb[1] & 0x1f) << 8 | dtf->dtf_tsb[2];
+ th_dvb_table_t *vec[tdmi->tdmi_num_tables], *tdt;
+ int i = 0;
+ LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) {
+ vec[i++] = tdt;
+ tdt->tdt_refcount++;
+ }
+ assert(i == tdmi->tdmi_num_tables);
+ int len = tdmi->tdmi_num_tables; // can change during callbacks
+ for(i = 0; i < len; i++) {
+ tdt = vec[i];
+ if(!tdt->tdt_destroyed) {
+ if(tdt->tdt_pid == pid)
+ psi_section_reassemble(&tdt->tdt_sect, dtf->dtf_tsb,
+ 0, got_section, tdt);
+ }
+ dvb_table_release(tdt);
+ }
}
+/**
+ *
+ */
+static void *
+dvb_table_input(void *aux)
+{
+ th_dvb_adapter_t *tda = aux;
+ th_dvb_mux_instance_t *tdmi;
+ dvb_table_feed_t *dtf;
+
+ while(1) {
+
+ pthread_mutex_lock(&tda->tda_delivery_mutex);
+
+ while((dtf = TAILQ_FIRST(&tda->tda_table_feed)) == NULL)
+ pthread_cond_wait(&tda->tda_table_feed_cond, &tda->tda_delivery_mutex);
+ TAILQ_REMOVE(&tda->tda_table_feed, dtf, dtf_link);
+
+ pthread_mutex_unlock(&tda->tda_delivery_mutex);
+
+ pthread_mutex_lock(&global_lock);
+
+ if((tdmi = tda->tda_mux_current) != NULL)
+ dvb_table_raw_dispatch(tdmi, dtf);
+
+ pthread_mutex_unlock(&global_lock);
+ free(dtf);
+ }
+ return NULL;
+}
+
+
/**
*
*/
tda->tda_close_service = close_service;
tda->tda_open_table = open_table;
tda->tda_close_table = close_table;
+
+ TAILQ_INIT(&tda->tda_table_feed);
+ pthread_cond_init(&tda->tda_table_feed_cond, NULL);
+
+ pthread_t ptid;
+ pthread_create(&ptid, NULL, dvb_table_input, tda);
+
}
/**
*
*/
-void
+static void
dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi)
{
th_dvb_table_t *tdt;
}
+/**
+ *
+ */
+void
+dvb_table_dispatch(uint8_t *sec, int r, th_dvb_table_t *tdt)
+{
+ if(tdt->tdt_destroyed)
+ return;
+
+ int chkcrc = tdt->tdt_flags & TDT_CRC;
+ int tableid, len;
+ uint8_t *ptr;
+ int ret;
+ th_dvb_mux_instance_t *tdmi = tdt->tdt_tdmi;
+
+ /* 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 && tvh_crc32(sec, r, 0xffffffff))
+ return;
+
+ r -= 3;
+ tableid = sec[0];
+ len = ((sec[1] & 0x0f) << 8) | sec[2];
+
+ if(len < r)
+ return;
+
+ if((tableid & tdt->tdt_mask) != tdt->tdt_table)
+ return;
+
+ ptr = &sec[3];
+ if(chkcrc) len -= 4; /* Strip trailing CRC */
+
+ if(tdt->tdt_flags & TDT_CA)
+ ret = tdt->tdt_callback((th_dvb_mux_instance_t *)tdt,
+ sec, len + 3, tableid, tdt->tdt_opaque);
+ else if(tdt->tdt_flags & TDT_TDT)
+ ret = tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt);
+ else
+ ret = tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque);
+
+ if(ret == 0)
+ tdt->tdt_count++;
+
+ if(tdt->tdt_flags & TDT_QUICKREQ)
+ dvb_table_fastswitch(tdmi);
+}
+
+
+/**
+ *
+ */
+void
+dvb_table_release(th_dvb_table_t *tdt)
+{
+ if(--tdt->tdt_refcount == 0)
+ free(tdt);
+}
+
+
/**
*
*/
dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi,
th_dvb_table_t *tdt)
{
+ lock_assert(&global_lock);
+ assert(tdt->tdt_tdmi == tdmi);
LIST_REMOVE(tdt, tdt_link);
+ tdmi->tdmi_num_tables--;
tda->tda_close_table(tdmi, tdt);
free(tdt->tdt_name);
- free(tdt);
+ tdt->tdt_destroyed = 1;
+ dvb_table_release(tdt);
}
-
-
/**
* Add a new DVB table
*/
tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask,
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)
+ const char *name, int flags, int pid)
{
th_dvb_table_t *t;
LIST_FOREACH(t, &tdmi->tdmi_tables, tdt_link) {
if(pid == t->tdt_pid &&
t->tdt_callback == callback && t->tdt_opaque == opaque) {
- if (tdt) free(tdt);
return;
}
}
-
- if(tdt == NULL)
- tdt = calloc(1, sizeof(th_dvb_table_t));
-
+ th_dvb_table_t *tdt = calloc(1, sizeof(th_dvb_table_t));
+ tdt->tdt_refcount = 1;
tdt->tdt_name = strdup(name);
tdt->tdt_callback = callback;
tdt->tdt_opaque = opaque;
tdt->tdt_flags = flags;
tdt->tdt_table = tableid;
tdt->tdt_mask = mask;
+ tdt->tdt_tdmi = tdmi;
LIST_INSERT_HEAD(&tdmi->tdmi_tables, tdt, tdt_link);
+ tdmi->tdmi_num_tables++;
tdt->tdt_fd = -1;
- TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
tdmi->tdmi_adapter->tda_open_table(tdmi, tdt);
}
break;
tdt_add(tdmi, 0, 0, dvb_ca_callback, (void *)caid, "CA",
- TDT_CA, pid, NULL);
+ TDT_CA, pid);
break;
default:
table = 0x40;
}
tdt_add(tdmi, table, 0xff, dvb_nit_callback, NULL, "nit",
- TDT_QUICKREQ | TDT_CRC, 0x10, NULL);
+ TDT_QUICKREQ | TDT_CRC, 0x10);
/* Service Descriptor Table and Bouqeut Allocation Table */
tdt_add(tdmi, 0, 0, dvb_pidx11_callback, NULL, "pidx11",
- TDT_QUICKREQ | TDT_CRC, 0x11, NULL);
+ TDT_QUICKREQ | TDT_CRC, 0x11);
}
}
tdt_add(tdmi, tableid, 0xff, atsc_vct_callback, NULL, "vct",
- TDT_QUICKREQ | TDT_CRC, 0x1ffb, NULL);
+ TDT_QUICKREQ | TDT_CRC, 0x1ffb);
}
{
/* Program Allocation Table */
tdt_add(tdmi, 0x00, 0xff, dvb_pat_callback, NULL, "pat",
- TDT_QUICKREQ | TDT_CRC, 0, NULL);
+ TDT_QUICKREQ | TDT_CRC, 0);
/* Conditional Access Table */
tdt_add(tdmi, 0x1, 0xff, dvb_cat_callback, NULL, "cat",
- TDT_CRC, 1, NULL);
+ TDT_CRC, 1);
switch(tdmi->tdmi_adapter->tda_type) {
snprintf(pmtname, sizeof(pmtname), "PMT(%d)", pmt_pid);
tdt_add(tdmi, 0x2, 0xff, dvb_pmt_callback, NULL, pmtname,
- TDT_CRC | TDT_QUICKREQ | TDT_TDT, pmt_pid, NULL);
+ TDT_CRC | TDT_QUICKREQ | TDT_TDT, pmt_pid);
}
void
tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3840, NULL);
tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3841, NULL);
#endif
- tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3002, NULL);
- tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3003, NULL);
+ tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3002);
+ tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3003);
/* Viasat Baltic (0x39) */
} else if (!strcmp("viasat_baltic", m->id)) {
- tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x39, NULL);
+ tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x39);
/* Standard (0x12) */
} else {
- tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x12, NULL);
+ tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x12);
}
tvhlog(LOG_DEBUG, m->id, "install table handlers");
}
while (*t) {
// TODO: what about 0x46 (service description)
tdt_add(tdmi, 0x4a, 0xff, _opentv_channel_callback, m,
- m->id, TDT_CRC, *t++, NULL);
+ m->id, TDT_CRC, *t++);
}
/* Titles */
while (*t) {
_opentv_status_get_pid(sta, *t);
tdt_add(tdmi, 0xa0, 0xfc, _opentv_title_callback, m,
- m->id, TDT_CRC | TDT_TDT, *t++, NULL);
+ m->id, TDT_CRC | TDT_TDT, *t++);
}
/* Summaries */
while (*t) {
_opentv_status_get_pid(sta, *t);
tdt_add(tdmi, 0xa8, 0xfc, _opentv_summary_callback, m,
- m->id, TDT_CRC | TDT_TDT, *t++, NULL);
+ m->id, TDT_CRC | TDT_TDT, *t++);
}
}
if(crc && tvh_crc32(ps->ps_data, tsize, 0xffffffff))
return -1;
- cb(ps->ps_data, tsize - (crc ? 4 : 0), opaque);
ps->ps_offset = 0;
+ cb(ps->ps_data, tsize - (crc ? 4 : 0), opaque);
return len - excess;
}