From 91ebc7cf405c00ba1d77b1064d3084ff6a3da255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 22 Jun 2009 21:18:09 +0000 Subject: [PATCH] If we are unable to create a demultiplex filter in hardware for a table, put the table on a pending queue and cycle through all tables. Fixes problems with low-end adapters that does not support that many filter in hardware. --- src/dvb/dvb.h | 1 + src/dvb/dvb_multiplex.c | 2 + src/dvb/dvb_tables.c | 273 ++++++++++++++++++++++++++-------------- 3 files changed, 184 insertions(+), 92 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 391cb686..fa62c874 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -56,6 +56,7 @@ typedef struct th_dvb_mux_instance { time_t tdmi_time; LIST_HEAD(, th_dvb_table) tdmi_tables; + TAILQ_HEAD(, th_dvb_table) tdmi_table_queue; enum { TDMI_FE_UNKNOWN, diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 132e09db..a9856c8d 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -128,6 +128,8 @@ dvb_mux_create(th_dvb_adapter_t *tda, struct dvb_frontend_parameters *fe_param, tdmi->tdmi_scan_queue = &tda->tda_scan_queues[DVB_MUX_SCAN_INITIAL]; TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); + TAILQ_INIT(&tdmi->tdmi_table_queue); + tdmi->tdmi_transport_stream_id = tsid; tdmi->tdmi_adapter = tda; tdmi->tdmi_network = network ? strdup(network) : NULL; diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 0fec3248..83232b75 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -43,29 +44,49 @@ #define TDT_CRC 0x1 #define TDT_QUICKREQ 0x2 -#define TDT_FREE_OPAQUE 0x4 -#define TDT_INC_TABLE_HDR 0x8 - -static int tid_tally; +#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; void (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tableid, void *opaque); - int tdt_fd; int tdt_count; - int tdt_id; int tdt_pid; - int tdt_flags; + + struct dmx_sct_filter_params *tdt_fparams; + + int tdt_id; + } th_dvb_table_t; @@ -105,6 +126,95 @@ dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi) } +/** + * + */ +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_INC_TABLE_HDR; + int tableid, len; + uint8_t *ptr; + + /* 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) + tdt->tdt_callback(tdmi, sec, len + 3, tableid, tdt->tdt_opaque); + else + tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque); + + dvb_table_fastswitch(tdmi); +} + /** * */ @@ -112,63 +222,48 @@ static void * dvb_table_input(void *aux) { th_dvb_adapter_t *tda = aux; - int r, i, tid, fd, tableid, len, x; + int r, i, tid, fd, x; struct epoll_event ev[1]; - uint8_t sec[4096], *ptr; + uint8_t sec[4096]; th_dvb_mux_instance_t *tdmi; th_dvb_table_t *tdt; - int chkcrc; 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); + tdmi = tda->tda_mux_current; - chkcrc = ev[i].data.u64 & 1; - fd = (ev[i].data.u64 >> 16) & 0xffff; - tid = ev[i].data.u64 >> 32; + LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) + if(tdt->tdt_id == tid) + break; - if(ev[i].events & EPOLLIN) { + if(tdt != NULL) { + dvb_proc_table(tdmi, tdt, sec, r); - if((r = read(fd, sec, sizeof(sec))) < 3) - continue; + /* Any tables pending (that wants a filter/fd) */ + if(TAILQ_FIRST(&tdmi->tdmi_table_queue) != NULL) { + tdt_close_fd(tdmi, tdt); - /* 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)) - continue; + tdt = TAILQ_FIRST(&tdmi->tdmi_table_queue); + assert(tdt != NULL); - r -= 3; - tableid = sec[0]; - len = ((sec[1] & 0x0f) << 8) | sec[2]; - - if(len < r) - continue; - - ptr = &sec[3]; - if(chkcrc) len -= 4; /* Strip trailing CRC */ - - pthread_mutex_lock(&global_lock); - - tdmi = tda->tda_mux_current; - - LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) - if(tdt->tdt_id == tid) - break; - - if(tdt != NULL) { - if(tdt->tdt_flags & TDT_INC_TABLE_HDR) - tdt->tdt_callback(tdmi, sec, len + 3, tableid, tdt->tdt_opaque); - else - tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque); - dvb_table_fastswitch(tdmi); + tdt_open_fd(tdmi, tdt); } - - pthread_mutex_unlock(&global_lock); - - } else { - fprintf(stderr, "%s: spurious poll event %x on fd %d\n", - tda->tda_identifier, ev[i].events, fd); } + + pthread_mutex_unlock(&global_lock); } } return NULL; @@ -187,19 +282,25 @@ dvb_table_init(th_dvb_adapter_t *tda) pthread_create(&ptid, NULL, dvb_table_input, tda); } + /** * */ static void -dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_table_t *tdt) +dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi, + th_dvb_table_t *tdt) { - epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL); - if(tdt->tdt_flags & TDT_FREE_OPAQUE) - free(tdt->tdt_opaque); + 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); - LIST_REMOVE(tdt, tdt_link); - close(tdt->tdt_fd); + free(tdt->tdt_fparams); free(tdt); } @@ -213,26 +314,16 @@ static void tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams, void (*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) + const char *name, int flags, int pid, th_dvb_table_t *tdt) { - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - th_dvb_table_t *tdt; - int fd; - struct epoll_event e; + th_dvb_table_t *t; - LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) - if(pid == tdt->tdt_pid) { + LIST_FOREACH(t, &tdmi->tdmi_tables, tdt_link) { + if(pid == t->tdt_pid) { + free(tdt); free(fparams); - if(flags & TDT_FREE_OPAQUE) - free(opaque); return; } - - if((fd = open(tda->tda_demux_path, O_RDWR)) == -1) { - free(fparams); - if(flags & TDT_FREE_OPAQUE) - free(opaque); - return; } if(fparams == NULL) @@ -242,24 +333,21 @@ tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams, fparams->flags |= DMX_IMMEDIATE_START; fparams->pid = pid; - tdt = calloc(1, sizeof(th_dvb_table_t)); - tdt->tdt_fd = fd; + + 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_id = ++tid_tally; tdt->tdt_pid = pid; tdt->tdt_flags = flags; - - e.events = EPOLLIN; - e.data.u64 = ((uint64_t)tdt->tdt_id << 32) | (fd << 16) | - (flags & TDT_CRC ? 1 : 0); - - epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_ADD, fd, &e); - - ioctl(fd, DMX_SET_FILTER, fparams); - free(fparams); + 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); } @@ -617,6 +705,7 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, typedef struct ca_stream { + th_dvb_table_t tdt; int cs_caid; } ca_stream_t; @@ -661,7 +750,7 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, cs = calloc(1, sizeof(ca_stream_t)); cs->cs_caid = caid; tdt_add(tdmi, NULL, dvb_ca_callback, cs, "CA", - TDT_FREE_OPAQUE | TDT_INC_TABLE_HDR, pid); + TDT_INC_TABLE_HDR, pid, &cs->tdt); break; default: @@ -905,7 +994,7 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi) fp->filter.filter[0] = 0x00; fp->filter.mask[0] = 0xff; tdt_add(tdmi, fp, dvb_pat_callback, NULL, "pat", - TDT_QUICKREQ | TDT_CRC, 0); + TDT_QUICKREQ | TDT_CRC, 0, NULL); /* Conditional Access Table */ @@ -913,7 +1002,7 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi) fp->filter.filter[0] = 0x1; fp->filter.mask[0] = 0xff; tdt_add(tdmi, fp, dvb_cat_callback, NULL, "cat", - TDT_CRC, 1); + TDT_CRC, 1, NULL); /* Network Information Table */ @@ -921,7 +1010,7 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi) fp->filter.filter[0] = 0x40; fp->filter.mask[0] = 0xff; tdt_add(tdmi, fp, dvb_nit_callback, NULL, "nit", - TDT_QUICKREQ | TDT_CRC, 0x10); + TDT_QUICKREQ | TDT_CRC, 0x10, NULL); /* Service Descriptor Table */ @@ -929,13 +1018,13 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi) fp->filter.filter[0] = 0x42; fp->filter.mask[0] = 0xff; tdt_add(tdmi, fp, dvb_sdt_callback, NULL, "sdt", - TDT_QUICKREQ | TDT_CRC, 0x11); + 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); + TDT_CRC, 0x12, NULL); /* Running Status Table */ @@ -943,7 +1032,7 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi) fp->filter.filter[0] = 0x71; fp->filter.mask[0] = 0xff; tdt_add(tdmi, fp, dvb_rst_callback, NULL, "rst", - TDT_CRC, 0x13); + TDT_CRC, 0x13, NULL); } @@ -964,7 +1053,7 @@ dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t, 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); + TDT_CRC | TDT_QUICKREQ, pmt_pid, NULL); } @@ -978,6 +1067,6 @@ dvb_table_flush_all(th_dvb_mux_instance_t *tdmi) th_dvb_table_t *tdt; while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL) - dvb_tdt_destroy(tda, tdt); + dvb_tdt_destroy(tda, tdmi, tdt); }