From 02dc5e5f37c8b99d520ccc37ebf2333fde2f53aa Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 13 Mar 2013 19:36:59 +0000 Subject: [PATCH] Fix #1643 - dvb: alter the way we handle dvb tuning This ensures that demux filters are not installed until after tuning is locked. This should resolve most stale data issues. (cherry picked from commit ddc466c1bfa7405563a68a662114f5a3659d7cb5) --- src/dvb/dvb.h | 14 +++- src/dvb/dvb_adapter.c | 169 +++++++++++++++++++++++++++--------------- src/dvb/dvb_fe.c | 25 +++++-- src/dvb/dvb_service.c | 11 ++- 4 files changed, 144 insertions(+), 75 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index aaccd946..335ac3c0 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -29,6 +29,12 @@ struct service; struct th_dvb_table; struct th_dvb_mux_instance; +#define TDA_OPT_FE 0x1 +#define TDA_OPT_DVR 0x2 +#define TDA_OPT_DMX 0x4 +#define TDA_OPT_PWR 0x8 +#define TDA_OPT_ALL (TDA_OPT_FE | TDA_OPT_DVR | TDA_OPT_DMX | TDA_OPT_PWR) + #define DVB_VER_INT(maj,min) (((maj) << 16) + (min)) #define DVB_VER_ATLEAST(maj, min) \ @@ -202,6 +208,8 @@ typedef struct th_dvb_adapter { uint32_t tda_enabled; + int tda_locked; + const char *tda_rootpath; char *tda_identifier; uint32_t tda_autodiscovery; @@ -342,11 +350,9 @@ void dvb_adapter_init(uint32_t adapter_mask, const char *rawfile); void dvb_adapter_mux_scanner(void *aux); -void dvb_adapter_start (th_dvb_adapter_t *tda); +void dvb_adapter_start (th_dvb_adapter_t *tda, int opt); -void dvb_adapter_stop (th_dvb_adapter_t *tda); - -void dvb_adapter_stop_dvr (th_dvb_adapter_t *tda); +void dvb_adapter_stop (th_dvb_adapter_t *tda, int opt); void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 8b3878dc..02b3fb8b 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -141,7 +141,7 @@ dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on) gtimer_disarm(&tda->tda_mux_scanner_timer); if (tda->tda_mux_current) dvb_fe_stop(tda->tda_mux_current, 0); - dvb_adapter_stop(tda); + dvb_adapter_stop(tda, TDA_OPT_ALL); } else { tda_init(tda); } @@ -692,22 +692,26 @@ static void tda_init (th_dvb_adapter_t *tda) * */ void -dvb_adapter_start ( th_dvb_adapter_t *tda ) +dvb_adapter_start ( th_dvb_adapter_t *tda, int opt ) { if(tda->tda_enabled == 0) { tvhlog(LOG_INFO, "dvb", "Adapter \"%s\" cannot be started - it's disabled", tda->tda_displayname); return; } + + /* Default to ALL */ + if (!opt) + opt = TDA_OPT_ALL; /* Open front end */ - if (tda->tda_fe_fd == -1) { + if ((opt & TDA_OPT_FE) && (tda->tda_fe_fd == -1)) { tda->tda_fe_fd = tvh_open(tda->tda_fe_path, O_RDWR | O_NONBLOCK, 0); if (tda->tda_fe_fd == -1) return; tvhlog(LOG_DEBUG, "dvb", "%s opened frontend %s", tda->tda_rootpath, tda->tda_fe_path); } /* Start DVR thread */ - if (tda->tda_dvr_pipe.rd == -1) { + if ((opt & TDA_OPT_DVR) && (tda->tda_dvr_pipe.rd == -1)) { int err = tvh_pipe(O_NONBLOCK, &tda->tda_dvr_pipe); assert(err != -1); pthread_create(&tda->tda_dvr_thread, NULL, dvb_adapter_input_dvr, tda); @@ -716,10 +720,14 @@ dvb_adapter_start ( th_dvb_adapter_t *tda ) } void -dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda ) +dvb_adapter_stop ( th_dvb_adapter_t *tda, int opt ) { + /* Poweroff */ + if (opt & TDA_OPT_PWR) + dvb_adapter_poweroff(tda); + /* Stop DVR thread */ - if (tda->tda_dvr_pipe.rd != -1) { + if ((opt & TDA_OPT_DVR) && (tda->tda_dvr_pipe.rd != -1)) { tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1); assert(!err); @@ -729,26 +737,17 @@ dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda ) tda->tda_dvr_pipe.rd = -1; tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); } -} -void -dvb_adapter_stop ( th_dvb_adapter_t *tda ) -{ - /* Poweroff */ - dvb_adapter_poweroff(tda); - - /* Don't stop/close */ + /* Don't close FE */ if (!tda->tda_idleclose && tda->tda_enabled) return; /* Close front end */ - if (tda->tda_fe_fd != -1) { + if ((opt & TDA_OPT_FE) && (tda->tda_fe_fd != -1)) { tvhlog(LOG_DEBUG, "dvb", "%s closing frontend", tda->tda_rootpath); close(tda->tda_fe_fd); tda->tda_fe_fd = -1; } - dvb_adapter_stop_dvr(tda); - dvb_adapter_notify(tda); } @@ -980,8 +979,39 @@ dvb_adapter_clean(th_dvb_adapter_t *tda) service_remove_subscriber(t, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN); } +/** + * Install RAW PES filter + */ +static int +dvb_adapter_raw_filter(th_dvb_adapter_t *tda) +{ + int dmx = -1; + struct dmx_pes_filter_params dmx_param; + dmx = tvh_open(tda->tda_demux_path, O_RDWR, 0); + if(dmx == -1) { + tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", + tda->tda_demux_path, strerror(errno)); + return -1; + } + memset(&dmx_param, 0, sizeof(dmx_param)); + dmx_param.pid = 0x2000; + dmx_param.input = DMX_IN_FRONTEND; + dmx_param.output = DMX_OUT_TS_TAP; + dmx_param.pes_type = DMX_PES_OTHER; + dmx_param.flags = DMX_IMMEDIATE_START; + + if(ioctl(dmx, DMX_SET_PES_FILTER, &dmx_param) == -1) { + tvhlog(LOG_ERR, "dvb", + "Unable to configure demuxer \"%s\" for all PIDs -- %s", + tda->tda_demux_path, strerror(errno)); + close(dmx); + return -1; + } + + return dmx; +} /** * @@ -990,54 +1020,18 @@ static void * dvb_adapter_input_dvr(void *aux) { th_dvb_adapter_t *tda = aux; - int fd, i, r, c, efd, nfds, dmx = -1; + th_dvb_mux_instance_t *tdmi; + int fd = -1, i, r, c, efd, nfds, dmx = -1; uint8_t tsb[188 * 10]; service_t *t; struct epoll_event ev; - - fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0); - if(fd == -1) { - tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", tda->tda_dvr_path, strerror(errno)); - return NULL; - } - - if(tda->tda_rawmode) { - - // Receive unfiltered raw transport stream - - dmx = tvh_open(tda->tda_demux_path, O_RDWR, 0); - if(dmx == -1) { - tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", - tda->tda_demux_path, strerror(errno)); - close(fd); - return NULL; - } - - struct dmx_pes_filter_params dmx_param; - - memset(&dmx_param, 0, sizeof(dmx_param)); - dmx_param.pid = 0x2000; - dmx_param.input = DMX_IN_FRONTEND; - dmx_param.output = DMX_OUT_TS_TAP; - dmx_param.pes_type = DMX_PES_OTHER; - dmx_param.flags = DMX_IMMEDIATE_START; - - if(ioctl(dmx, DMX_SET_PES_FILTER, &dmx_param)) { - tvhlog(LOG_ERR, "dvb", - "Unable to configure demuxer \"%s\" for all PIDs -- %s", - tda->tda_demux_path, strerror(errno)); - close(dmx); - close(fd); - return NULL; - } - } + int delay = 10, locked = 0; + fe_status_t festat; /* Create poll */ efd = epoll_create(2); memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; - ev.data.fd = fd; - epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); ev.data.fd = tda->tda_dvr_pipe.rd; epoll_ctl(efd, EPOLL_CTL_ADD, tda->tda_dvr_pipe.rd, &ev); @@ -1045,10 +1039,65 @@ dvb_adapter_input_dvr(void *aux) while(1) { /* Wait for input */ - nfds = epoll_wait(efd, &ev, 1, -1); - if (nfds < 1) continue; - if (ev.data.fd != fd) break; + nfds = epoll_wait(efd, &ev, 1, delay); + /* Exit */ + if ((nfds > 0) && (ev.data.fd != fd)) break; + + /* Check for lock */ + if (!locked) { + if (ioctl(tda->tda_fe_fd, FE_READ_STATUS, &festat)) + continue; + if (!(festat & FE_HAS_LOCK)) + continue; + + /* Open DVR */ + fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0); + if (fd == -1) { + tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", + tda->tda_dvr_path, strerror(errno)); + break; + } + ev.data.fd = fd; + epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); + + /* Note: table handlers must be installed with global lock */ + pthread_mutex_lock(&global_lock); + tda->tda_locked = locked = 1; + delay = -1; + if ((tdmi = tda->tda_mux_current)) { + + /* Install table handlers */ + dvb_table_add_default(tdmi); + epggrab_mux_start(tdmi); + + /* Raw filter */ + if(tda->tda_rawmode) + dmx = dvb_adapter_raw_filter(tda); + + /* Service filters */ + pthread_mutex_lock(&tda->tda_delivery_mutex); + LIST_FOREACH(t, &tda->tda_transports, s_active_link) { + if (t->s_dvb_mux_instance == tdmi) { + tda->tda_open_service(tda, t); + dvb_table_add_pmt(tdmi, t->s_pmt_pid); + } + } + pthread_mutex_unlock(&tda->tda_delivery_mutex); + } + pthread_mutex_unlock(&global_lock); + + /* Error */ + if (tda->tda_rawmode && (dmx == -1)) { + tvhlog(LOG_ALERT, "dvb", "Unable to install raw mux filter"); + break; + } + } + + /* No data */ + if (nfds < 1) continue; + + /* Read data */ c = read(fd, tsb+r, sizeof(tsb)-r); if (c < 0) { if (errno == EAGAIN || errno == EINTR) diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 77f76217..d0924c69 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -273,6 +273,7 @@ void dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + dvb_table_feed_t *dtf; lock_assert(&global_lock); @@ -285,8 +286,13 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) dvb_mux_save(tdmi); } - dvb_adapter_stop_dvr(tda); + dvb_adapter_stop(tda, TDA_OPT_DVR); + pthread_mutex_lock(&tda->tda_delivery_mutex); + while((dtf = TAILQ_FIRST(&tda->tda_table_feed))) + TAILQ_REMOVE(&tda->tda_table_feed, dtf, dtf_link); + pthread_mutex_unlock(&tda->tda_delivery_mutex); dvb_table_flush_all(tdmi); + tda->tda_locked = 0; assert(tdmi->tdmi_scan_queue == NULL); @@ -300,7 +306,7 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) if (!retune) { gtimer_disarm(&tda->tda_fe_monitor_timer); - dvb_adapter_stop(tda); + dvb_adapter_stop(tda, TDA_OPT_ALL); } } @@ -421,9 +427,11 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) char buf[256]; int r; - lock_assert(&global_lock); + if(tda->tda_enabled == 0) + return SM_CODE_TUNING_FAILED; + if(tda->tda_mux_current == tdmi) return 0; @@ -434,7 +442,8 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) if(tda->tda_mux_current != NULL) dvb_fe_stop(tda->tda_mux_current, 1); - dvb_adapter_start(tda); + + dvb_adapter_start(tda, TDA_OPT_FE | TDA_OPT_PWR); if(tda->tda_type == FE_QPSK) { @@ -486,7 +495,6 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) tda->tda_fe_monitor_hold = 4; - #if DVB_API_VERSION >= 5 if (tda->tda_type == FE_QPSK) { tvhlog(LOG_DEBUG, "dvb", "\"%s\" tuning via s2api to \"%s\" (%d, %d Baud, " @@ -517,15 +525,18 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) if (errno == EINVAL) dvb_mux_set_enable(tdmi, 0); return SM_CODE_TUNING_FAILED; - } + } tda->tda_mux_current = tdmi; + dvb_adapter_start(tda, TDA_OPT_ALL); + gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); - +#if 0 dvb_table_add_default(tdmi); epggrab_mux_start(tdmi); +#endif dvb_adapter_notify(tda); return 0; diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 61242bef..31e25e64 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -86,18 +86,21 @@ dvb_service_start(service_t *t, unsigned int weight, int force_start) dvb_adapter_clean(tda); } + r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start"); + pthread_mutex_lock(&tda->tda_delivery_mutex); - r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start"); if(!r) LIST_INSERT_HEAD(&tda->tda_transports, t, s_active_link); pthread_mutex_unlock(&tda->tda_delivery_mutex); - if(!r) - tda->tda_open_service(tda, t); + if (tda->tda_locked) { + if(!r) + tda->tda_open_service(tda, t); - dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid); + dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid); + } return r; }