From f4fc4380314c1e03cb3a3dbd6bf13fa18371e8b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 22 Oct 2012 11:26:24 +0200 Subject: [PATCH] dvb: Add table parsing when in raw mode --- src/dvb/dvb.h | 40 +++++++++++++-- src/dvb/dvb_adapter.c | 18 ++++++- src/dvb/dvb_fe.c | 2 + src/dvb/dvb_input_filtered.c | 47 +----------------- src/dvb/dvb_input_raw.c | 88 +++++++++++++++++++++++++++++++++ src/dvb/dvb_tables.c | 96 +++++++++++++++++++++++++++++------- src/epggrab/module/eit.c | 8 +-- src/epggrab/module/opentv.c | 6 +-- src/psi.c | 2 +- 9 files changed, 232 insertions(+), 75 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 4cdc7e4d..8d7929a9 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -23,6 +23,7 @@ #include #include #include "htsmsg.h" +#include "psi.h" struct service; struct th_dvb_table; @@ -107,6 +108,8 @@ typedef struct th_dvb_mux_instance { LIST_HEAD(, th_dvb_table) tdmi_tables; + int tdmi_num_tables; + TAILQ_HEAD(, th_dvb_table) tdmi_table_queue; int tdmi_table_initial; @@ -150,6 +153,22 @@ typedef struct th_dvb_mux_instance { } 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) */ @@ -236,6 +255,14 @@ typedef struct th_dvb_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; /** @@ -262,6 +289,7 @@ typedef struct th_dvb_table { int tdt_fd; LIST_ENTRY(th_dvb_table) tdt_link; + th_dvb_mux_instance_t *tdt_tdmi; char *tdt_name; @@ -278,6 +306,10 @@ typedef struct th_dvb_table { int tdt_table; int tdt_mask; + int tdt_destroyed; + int tdt_refcount; + + psi_section_t tdt_sect; // Manual reassembly } th_dvb_table_t; @@ -459,12 +491,10 @@ void dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid); 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, @@ -475,6 +505,10 @@ int dvb_pidx11_callback #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 */ diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 73c48b6b..c3ad82b7 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -451,7 +451,7 @@ tda_add(int adapter_num) 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); @@ -796,12 +796,25 @@ dvb_adapter_input_dvr(void *aux) /* 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); @@ -816,6 +829,9 @@ dvb_adapter_input_dvr(void *aux) } } + if(wakeup_table_feed) + pthread_cond_signal(&tda->tda_table_feed_cond); + pthread_mutex_unlock(&tda->tda_delivery_mutex); /* reset buffer */ diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 321ae860..1ebfda3a 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -226,6 +226,8 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + lock_assert(&global_lock); + assert(tdmi == tda->tda_mux_current); tda->tda_mux_current = NULL; diff --git a/src/dvb/dvb_input_filtered.c b/src/dvb/dvb_input_filtered.c index 468b524f..a4dddf26 100644 --- a/src/dvb/dvb_input_filtered.c +++ b/src/dvb/dvb_input_filtered.c @@ -116,9 +116,6 @@ open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) 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) { @@ -173,47 +170,6 @@ tdt_close_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) } -/** - * - */ -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); -} /** * @@ -250,7 +206,7 @@ dvb_table_input(void *aux) 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 && @@ -259,6 +215,7 @@ dvb_table_input(void *aux) 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); } diff --git a/src/dvb/dvb_input_raw.c b/src/dvb/dvb_input_raw.c index b1677805..0c3f2a56 100644 --- a/src/dvb/dvb_input_raw.c +++ b/src/dvb/dvb_input_raw.c @@ -42,6 +42,7 @@ static void open_service(th_dvb_adapter_t *tda, service_t *s) { + // NOP -- We receive all PIDs anyway } @@ -53,6 +54,7 @@ open_service(th_dvb_adapter_t *tda, service_t *s) static void close_service(th_dvb_adapter_t *tda, service_t *s) { + // NOP -- open_service() is a NOP } @@ -62,6 +64,9 @@ close_service(th_dvb_adapter_t *tda, service_t *s) 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; } @@ -71,8 +76,84 @@ open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) 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; +} + + /** * */ @@ -84,5 +165,12 @@ dvb_input_raw_setup(th_dvb_adapter_t *tda) 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); + } diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index f292b4e5..529f5c50 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -46,7 +46,7 @@ /** * */ -void +static void dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi) { th_dvb_table_t *tdt; @@ -72,6 +72,66 @@ dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi) } +/** + * + */ +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); +} + + /** * */ @@ -79,15 +139,17 @@ static void 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 */ @@ -95,7 +157,7 @@ void 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; @@ -106,15 +168,12 @@ tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask, 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; @@ -122,9 +181,10 @@ tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask, 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); } @@ -510,7 +570,7 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, break; tdt_add(tdmi, 0, 0, dvb_ca_callback, (void *)caid, "CA", - TDT_CA, pid, NULL); + TDT_CA, pid); break; default: @@ -959,12 +1019,12 @@ dvb_table_add_default_dvb(th_dvb_mux_instance_t *tdmi) 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); } @@ -983,7 +1043,7 @@ dvb_table_add_default_atsc(th_dvb_mux_instance_t *tdmi) } tdt_add(tdmi, tableid, 0xff, atsc_vct_callback, NULL, "vct", - TDT_QUICKREQ | TDT_CRC, 0x1ffb, NULL); + TDT_QUICKREQ | TDT_CRC, 0x1ffb); } @@ -997,11 +1057,11 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi) { /* 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) { @@ -1028,7 +1088,7 @@ dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid) 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 diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index abad1ec2..cb5bffd3 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -842,16 +842,16 @@ static void _eit_start 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"); } diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index 8b6010aa..0778ca82 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -669,7 +669,7 @@ static void _opentv_start 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 */ @@ -677,7 +677,7 @@ static void _opentv_start 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 */ @@ -685,7 +685,7 @@ static void _opentv_start 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++); } } diff --git a/src/psi.c b/src/psi.c index 7960ae2b..dcfec72a 100644 --- a/src/psi.c +++ b/src/psi.c @@ -63,8 +63,8 @@ psi_section_reassemble0(psi_section_t *ps, const uint8_t *data, 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; }