diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index b1fb734e..3d8b0652 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -21,6 +21,7 @@ #include #include +#include #include "htsmsg.h" @@ -184,6 +185,7 @@ typedef struct th_dvb_adapter { uint32_t tda_disable_pmt_monitor; char *tda_displayname; + char *tda_fe_path; int tda_fe_fd; int tda_type; struct dvb_frontend_info *tda_fe_info; @@ -192,7 +194,9 @@ typedef struct th_dvb_adapter { char *tda_demux_path; - char *tda_dvr_path; + char *tda_dvr_path; + pthread_t tda_dvr_thread; + int tda_dvr_pipe[2]; int tda_hostconnection; @@ -279,6 +283,10 @@ void dvb_adapter_init(uint32_t adapter_mask); void dvb_adapter_mux_scanner(void *aux); +void dvb_adapter_start (th_dvb_adapter_t *tda); + +void dvb_adapter_stop (th_dvb_adapter_t *tda); + void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s); void dvb_adapter_set_auto_discovery(th_dvb_adapter_t *tda, int on); @@ -404,7 +412,7 @@ htsmsg_t *dvb_transport_build_msg(struct service *t); */ int dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason); -void dvb_fe_stop(th_dvb_mux_instance_t *tdmi); +void dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune); /** diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index cb5ae097..9e714e93 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -16,12 +16,14 @@ * along with this program. If not, see . */ +#define _GNU_SOURCE #include #include #include #include #include +#include #include #include #include @@ -368,12 +370,10 @@ dvb_adapter_checkspeed(th_dvb_adapter_t *tda) static void tda_add(int adapter_num) { - char path[200]; - char fname[256]; + char path[200], fname[256]; int fe, i, r; th_dvb_adapter_t *tda; char buf[400]; - pthread_t ptid; snprintf(path, sizeof(path), "/dev/dvb/adapter%d", adapter_num); snprintf(fname, sizeof(fname), "%s/frontend0", path); @@ -394,18 +394,20 @@ tda_add(int adapter_num) snprintf(tda->tda_demux_path, 256, "%s/demux0", path); tda->tda_dvr_path = malloc(256); snprintf(tda->tda_dvr_path, 256, "%s/dvr0", path); + tda->tda_fe_path = strdup(fname); - - tda->tda_fe_fd = fe; + tda->tda_fe_fd = -1; + tda->tda_dvr_pipe[0] = -1; tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info)); - if(ioctl(tda->tda_fe_fd, FE_GET_INFO, tda->tda_fe_info)) { + if(ioctl(fe, FE_GET_INFO, tda->tda_fe_info)) { tvhlog(LOG_ALERT, "dvb", "%s: Unable to query adapter", fname); close(fe); free(tda); return; } + close(fe); tda->tda_type = tda->tda_fe_info->type; @@ -437,7 +439,6 @@ tda_add(int adapter_num) TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); - pthread_create(&ptid, NULL, dvb_adapter_input_dvr, tda); dvb_table_init(tda); @@ -447,6 +448,48 @@ tda_add(int adapter_num) gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1); } +void +dvb_adapter_start ( th_dvb_adapter_t *tda ) +{ + /* Open front end */ + if (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[0] == -1) { + assert(pipe2(tda->tda_dvr_pipe, O_NONBLOCK | O_CLOEXEC) != -1); + pthread_create(&tda->tda_dvr_thread, NULL, dvb_adapter_input_dvr, tda); + tvhlog(LOG_DEBUG, "dvb", "%s started dvr thread", tda->tda_rootpath); + } +} + +void +dvb_adapter_stop ( th_dvb_adapter_t *tda ) +{ + /* Poweroff */ + dvb_adapter_poweroff(tda); + + /* Close front end */ + if (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; + } + + /* Stop DVR thread */ + if (tda->tda_dvr_pipe[0] != -1) { + tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); + assert(write(tda->tda_dvr_pipe[1], "", 1) == 1); + pthread_join(tda->tda_dvr_thread, NULL); + close(tda->tda_dvr_pipe[0]); + close(tda->tda_dvr_pipe[1]); + tda->tda_dvr_pipe[0] = -1; + tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); + } +} /** * @@ -581,8 +624,7 @@ dvb_adapter_mux_scanner(void *aux) /* Ensure we stop current mux and power off (if required) */ if (tda->tda_mux_current) - dvb_fe_stop(tda->tda_mux_current); - dvb_adapter_poweroff(tda); + dvb_fe_stop(tda->tda_mux_current, 0); } /** @@ -659,41 +701,58 @@ static void * dvb_adapter_input_dvr(void *aux) { th_dvb_adapter_t *tda = aux; - int fd, i, r; + int fd, i, r, efd, nfds; uint8_t tsb[188 * 10]; service_t *t; + struct epoll_event ev; - fd = tvh_open(tda->tda_dvr_path, O_RDONLY, 0); + fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0); if(fd == -1) { tvhlog(LOG_ALERT, "dvb", "%s: unable to open dvr", tda->tda_dvr_path); return NULL; } + /* Create poll */ + efd = epoll_create(2); + ev.events = EPOLLIN; + ev.data.fd = fd; + epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); + ev.data.fd = tda->tda_dvr_pipe[0]; + epoll_ctl(efd, EPOLL_CTL_ADD, tda->tda_dvr_pipe[0], &ev); + + while(1){ + + /* Wait for input */ + nfds = epoll_wait(efd, &ev, 1, -1); + if (nfds < 1) continue; + if (ev.data.fd != fd) break; - while(1) { r = read(fd, tsb, sizeof(tsb)); pthread_mutex_lock(&tda->tda_delivery_mutex); for(i = 0; i < r; i += 188) { 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(t->s_dvb_mux_instance == tda->tda_mux_current) + ts_recv_packet1(t, tsb + i, NULL); } if(tda->tda_dump_fd != -1) { if(write(tda->tda_dump_fd, tsb, r) != r) { - tvhlog(LOG_ERR, "dvb", + tvhlog(LOG_ERR, "dvb", "\"%s\" unable to write to mux dump file -- %s", tda->tda_identifier, strerror(errno)); - - close(tda->tda_dump_fd); - tda->tda_dump_fd = -1; - } + close(tda->tda_dump_fd); + tda->tda_dump_fd = -1; + } } pthread_mutex_unlock(&tda->tda_delivery_mutex); } + + close(efd); + close(fd); + return NULL; } diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 8044100a..34a379f6 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -201,7 +201,7 @@ dvb_fe_monitor(void *aux) * Stop the given TDMI */ void -dvb_fe_stop(th_dvb_mux_instance_t *tdmi) +dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; @@ -233,8 +233,13 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi) } epggrab_mux_stop(tdmi, 0); - + time(&tdmi->tdmi_lost_adapter); + + if (!retune) { + gtimer_disarm(&tda->tda_fe_monitor_timer); + dvb_adapter_stop(tda); + } } @@ -439,9 +444,10 @@ 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); - - + dvb_fe_stop(tda->tda_mux_current, 1); + else + dvb_adapter_start(tda); + if(tda->tda_type == FE_QPSK) { /* DVB-S */ diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 0172403e..a3d77479 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -317,7 +317,7 @@ dvb_mux_destroy(th_dvb_mux_instance_t *tdmi) dvb_transport_notify_by_adapter(tda); if(tda->tda_mux_current == tdmi) - dvb_fe_stop(tda->tda_mux_current); + dvb_fe_stop(tda->tda_mux_current, 0); if(tdmi->tdmi_conf.dmc_satconf != NULL) LIST_REMOVE(tdmi, tdmi_satconf_link);