From 578c3fc545ae4e9e69bc027d13aa453a42b8a508 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 30 May 2013 16:09:47 +0100 Subject: [PATCH] mpegts linuxdvb: starting to get working scanning on DVB-T Moved DVB mux config into dvb_support Updated table processing to include proper section tracking Fixed some problem in the MPEG-TS core Updates to get scanning working in LinuxDVB --- src/input/mpegts.h | 15 +- src/input/mpegts/dvb.h | 5 + src/input/mpegts/dvb_psi.c | 156 ++++++++++------ src/input/mpegts/dvb_support.c | 167 +++++++++++++++++- src/input/mpegts/linuxdvb/linuxdvb_device.c | 2 +- src/input/mpegts/linuxdvb/linuxdvb_frontend.c | 21 ++- src/input/mpegts/linuxdvb/linuxdvb_hardware.c | 6 +- src/input/mpegts/linuxdvb/linuxdvb_mux.c | 93 +++------- src/input/mpegts/linuxdvb/linuxdvb_network.c | 19 +- src/input/mpegts/linuxdvb/linuxdvb_private.h | 6 +- src/input/mpegts/mpegts_input.c | 8 + src/input/mpegts/mpegts_mux.c | 37 +++- src/input/mpegts/mpegts_table.c | 6 +- 13 files changed, 395 insertions(+), 146 deletions(-) diff --git a/src/input/mpegts.h b/src/input/mpegts.h index c6434cde..bb2e1a47 100644 --- a/src/input/mpegts.h +++ b/src/input/mpegts.h @@ -93,9 +93,14 @@ struct mpegts_table void *mt_opaque; mpegts_table_callback_t mt_callback; + struct { + int section; + int version; + int complete; + } mt_state[256]; - // TODO: remind myself of what each field is for int mt_count; + int mt_pid; int mt_id; @@ -169,7 +174,8 @@ struct mpegts_network (mpegts_mux_t*, uint16_t onid, uint16_t tsid, dvb_mux_conf_t *conf); mpegts_service_t* (*mn_create_service) (mpegts_mux_t*, uint16_t sid, uint16_t pmt_pid); - void (*mn_config_save) (mpegts_network_t*); + void (*mn_display_name) (mpegts_network_t*, char *buf, size_t len); + void (*mn_config_save) (mpegts_network_t*); // Note: the above are slightly odd in that they take mux instead of // network as initial param. This is intentional as we need to @@ -248,6 +254,7 @@ struct mpegts_mux void (*mm_open_table) (mpegts_mux_t*,mpegts_table_t*); void (*mm_close_table) (mpegts_mux_t*,mpegts_table_t*); void (*mm_create_instances) (mpegts_mux_t*); + void (*mm_display_name) (mpegts_mux_t*, char *buf, size_t len); /* * Fields @@ -364,6 +371,8 @@ struct mpegts_input { idnode_t mi_id; + int mi_enabled; + int mi_instance; LIST_ENTRY(mpegts_input) mi_global_link; @@ -403,6 +412,8 @@ struct mpegts_input void (*mi_close_service) (mpegts_input_t*,mpegts_service_t*); int (*mi_is_free) (mpegts_input_t*); int (*mi_current_weight) (mpegts_input_t*); + int (*mi_is_enabled) (mpegts_input_t*); + void (*mi_display_name) (mpegts_input_t*, char *buf, size_t len); }; #endif /* __TVH_MPEGTS_H__ */ diff --git a/src/input/mpegts/dvb.h b/src/input/mpegts/dvb.h index 2a058a83..64e7f10c 100644 --- a/src/input/mpegts/dvb.h +++ b/src/input/mpegts/dvb.h @@ -198,6 +198,11 @@ typedef struct dvb_mux_conf #endif } dvb_mux_conf_t; +const char *dvb_mux_conf_load + ( fe_type_t type, dvb_mux_conf_t *dmc, htsmsg_t *m ); +int dvb_mux_conf_save + ( dvb_mux_conf_t *dmc, htsmsg_t *m ); + /* conversion routines */ const char *dvb_rolloff2str ( int rolloff ); const char *dvb_delsys2str ( int delsys ); diff --git a/src/input/mpegts/dvb_psi.c b/src/input/mpegts/dvb_psi.c index d24d35ec..b7469082 100644 --- a/src/input/mpegts/dvb_psi.c +++ b/src/input/mpegts/dvb_psi.c @@ -370,27 +370,82 @@ dvb_desc_local_channel * *************************************************************************/ /* - * PAT processing + * Begin table */ - -int -dvb_pat_callback - (mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid) +static int +dvb_table_begin + (mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid, + int minlen, int *sect, int *last, int *ver) { - uint16_t sid, pid, tsid; - uint16_t nit_pid = DVB_NIT_PID; - mpegts_mux_t *mm = mt->mt_mux; - tvhtrace("pat", "tableid %02X len %d", tableid, len); - tvhlog_hexdump("pat", ptr, len); - - /* Not enough data */ - if(len < 5) + /* Not long enough */ + if (len < minlen) return -1; /* Ignore next */ if((ptr[2] & 1) == 0) return -1; + tvhtrace(mt->mt_name, "tableid %02X len %d", tableid, len); + tvhlog_hexdump(mt->mt_name, ptr, len); + + /* Section info */ + if (sect) { + *sect = ptr[3]; + *last = ptr[4]; + *ver = (ptr[2] >> 1) & 0x1F; + tvhtrace(mt->mt_name, " section %d last %d ver %d", *sect, *last, *ver); + + /* New version */ + if (mt->mt_state[tableid].complete && mt->mt_state[tableid].version != *ver) { + tvhtrace(mt->mt_name, " new version"); + mt->mt_state[tableid].complete = 0; + mt->mt_state[tableid].section = 0; + } + + /* Ignore */ + if (mt->mt_state[tableid].complete) { + tvhtrace(mt->mt_name, " already processed"); + return -1; + } + + /* Not the right section */ + tvhtrace(mt->mt_name, " waiting for section %d version %d", mt->mt_state[tableid].section, mt->mt_state[tableid].version); + if (mt->mt_state[tableid].section != *sect) { + tvhtrace(mt->mt_name, " skip, wrong section"); + return -1; + } + } + + return 0; +} + +static void +dvb_table_end + (mpegts_table_t *mt, int tableid, int sect, int last, int ver) +{ + mt->mt_state[tableid].section = sect + 1; + mt->mt_state[tableid].version = ver; + if (sect == last) + mt->mt_state[tableid].complete = 1; +} + +/* + * PAT processing + */ +int +dvb_pat_callback + (mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid) +{ + int sect, last, ver; + uint16_t sid, pid, tsid; + uint16_t nit_pid = DVB_NIT_PID; + mpegts_mux_t *mm = mt->mt_mux; + + /* Begin */ + if (tableid != 0) return -1; + if (dvb_table_begin(mt, ptr, len, tableid, 5, §, &last, &ver) == -1) + return -1; + /* Multiplex */ tsid = (ptr[0] << 8) | ptr[1]; tvhtrace("pat", "tsid %04X (%d)", tsid, tsid); @@ -431,7 +486,8 @@ dvb_pat_callback mpegts_table_add(mm, DVB_NIT_BASE, DVB_NIT_MASK, dvb_nit_callback, NULL, "nit", MT_CRC | MT_QUICKREQ, nit_pid); - return 0; + dvb_table_end(mt, tableid, sect, last, ver); + return mt->mt_state[0].complete ? 0 : -1; } /* @@ -442,10 +498,13 @@ int dvb_pmt_callback (mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid) { + int sect, last, ver; mpegts_mux_t *mm = mt->mt_mux; mpegts_service_t *s; - tvhtrace("pmt", "tableid %02X len %d", tableid, len); - tvhlog_hexdump("pmt", ptr, len); + + /* Start */ + if (dvb_table_begin(mt, ptr, len, tableid, 5, §, &last, &ver)) + return -1; LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link) { pthread_mutex_lock(&s->s_stream_mutex); @@ -463,7 +522,9 @@ dvb_pmt_callback dm->dm_current_tdmi, tdt); #endif - return 0; + /* Finish */ + dvb_table_end(mt, tableid, sect, last, ver); + return (sect == last) ? 0 : -1; } /* @@ -474,6 +535,7 @@ dvb_nit_callback (mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid) { int save; + int sect, last, ver; uint8_t dlen, dtag; uint16_t llen, dllen; const uint8_t *lptr, *dlptr, *dptr; @@ -481,19 +543,10 @@ dvb_nit_callback mpegts_mux_t *mm = mt->mt_mux, *mux; mpegts_network_t *mn = mm->mm_network; char name[256], dauth[256]; - const char *dstr = (tableid == 0x4A) ? "bat" : "nit"; - tvhtrace(dstr, "tableid %02X len %d", tableid, len); - tvhlog_hexdump(dstr, ptr, len); - - /* Not long enough */ - if (len < 7) - return -1; - - tvhtrace(dstr, " section %d last %d current %d", ptr[3], ptr[4], ptr[2]&1); - - /* Ignore "next" */ - if (!(ptr[2] & 0x01)) + /* Begin */ + if (tableid != 0x40 && tableid != 0x41 && tableid != 0x4A) return -1; + if (dvb_table_begin(mt, ptr, len, tableid, 7, §, &last, &ver)) return -1; /* BAT */ @@ -518,7 +571,7 @@ dvb_nit_callback /* Network Descriptors */ *name = 0; DVB_DESC_FOREACH(ptr, len, 5, lptr, llen, dtag, dlen, dptr) { - tvhtrace(dstr, " dtag %02X dlen %d", dtag, dlen); + tvhtrace(mt->mt_name, " dtag %02X dlen %d", dtag, dlen); switch (dtag) { case DVB_DESC_BOUQUET_NAME: @@ -534,11 +587,11 @@ dvb_nit_callback /* BAT */ if (tableid == 0x4A) { - tvhtrace("bat", "bouquet %04X (%d) [%s]", bid, bid, name); + tvhtrace(mt->mt_name, "bouquet %04X (%d) [%s]", bid, bid, name); /* NIT */ } else { - tvhtrace("nit", "network %04X (%d) [%s]", nid, nid, name); + tvhtrace(mt->mt_name, "network %04X (%d) [%s]", nid, nid, name); save = mpegts_network_set_nid(mn, nid); save |= mpegts_network_set_network_name(mn, name); if (save) @@ -549,7 +602,7 @@ dvb_nit_callback DVB_LOOP_FOREACH(ptr, len, 0, lptr, llen, 6) { tsid = (lptr[0] << 8) | lptr[1]; onid = (lptr[2] << 8) | lptr[3]; - tvhtrace(dstr, " onid %04X (%d) tsid %04X (%d)", onid, onid, tsid, tsid); + tvhtrace(mt->mt_name, " onid %04X (%d) tsid %04X (%d)", onid, onid, tsid, tsid); /* Find existing mux */ LIST_FOREACH(mux, &mn->mn_muxes, mm_network_link) @@ -557,7 +610,7 @@ dvb_nit_callback break; DVB_DESC_FOREACH(lptr, llen, 4, dlptr, dllen, dtag, dlen, dptr) { - tvhtrace(dstr, " dtag %02X dlen %d", dtag, dlen); + tvhtrace(mt->mt_name, " dtag %02X dlen %d", dtag, dlen); switch (dtag) { @@ -576,23 +629,25 @@ dvb_nit_callback case DVB_DESC_DEF_AUTHORITY: if (dvb_desc_def_authority(dptr, dlen, dauth, sizeof(dauth))) return -1; - tvhtrace(dstr, " default auth [%s]", dauth); + tvhtrace(mt->mt_name, " default auth [%s]", dauth); if (mux && *dauth) mpegts_mux_set_default_authority(mux, dauth); break; case DVB_DESC_LOCAL_CHAN: - if (dvb_desc_local_channel(dstr, dptr, dlen, mux)) + if (dvb_desc_local_channel(mt->mt_name, dptr, dlen, mux)) return -1; break; case DVB_DESC_SERVICE_LIST: - if (dvb_desc_service_list(dstr, dptr, dlen, mux)) + if (dvb_desc_service_list(mt->mt_name, dptr, dlen, mux)) return -1; break; } } } - return 0; + /* End */ + dvb_table_end(mt, tableid, sect, last, ver); + return (sect == last) ? 0 : -1; } /** @@ -602,28 +657,16 @@ int dvb_sdt_callback (mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid) { + int sect, last, ver; uint16_t onid, tsid; uint16_t llen; uint8_t dtag, dlen; const uint8_t *lptr, *dptr; mpegts_mux_t *mm = mt->mt_mux; - tvhtrace("sdt", "tableid %02X len %d", tableid, len); - tvhlog_hexdump("sdt", ptr, len); - - /* Not enough data */ - if(len < 8) - return -1; - - tvhtrace("sdt", " section %d last %d current %d", ptr[3], ptr[4], ptr[2]&1); - - /* Ignore next */ - if((ptr[2] & 1) == 0) - return -1; - - /* Validate */ - if (tableid != 0x42 && tableid != 0x46) - return -1; + /* Begin */ + if (tableid != 0x42 && tableid != 0x46) return -1; + dvb_table_begin(mt, ptr, len, tableid, 8, §, &last, &ver); /* ID */ tsid = ptr[0] << 8 | ptr[1]; @@ -742,7 +785,10 @@ dvb_sdt_callback service_refresh_channel((service_t*)s); } } - return 0; + + /* Done */ + dvb_table_end(mt, tableid, sect, last, ver); + return (mt->mt_state[0x42].complete && mt->mt_state[0x46].complete) ? 0 : -1; } /* diff --git a/src/input/mpegts/dvb_support.c b/src/input/mpegts/dvb_support.c index 828329e1..45a009cf 100644 --- a/src/input/mpegts/dvb_support.c +++ b/src/input/mpegts/dvb_support.c @@ -406,7 +406,9 @@ const static struct strtab delsystab[] = { { "SYS_ATSCMH", SYS_ATSCMH }, { "SYS_DMBTH", SYS_DMBTH }, { "SYS_CMMB", SYS_CMMB }, - { "SYS_DAB", SYS_DAB } + { "SYS_DAB", SYS_DAB }, + { "SYS_DVBT2", SYS_DVBT2 }, + { "SYS_TURBO", SYS_TURBO } #endif }; dvb_str2val(delsys); @@ -511,8 +513,167 @@ const static struct strtab typetab[] = { {"ATSC", FE_ATSC}, }; dvb_str2val(type); - - #undef dvb_str2val +/* + * Process mux conf + */ +static const char * +dvb_mux_conf_load_dvbt ( dvb_mux_conf_t *dmc, htsmsg_t *m ) +{ + int r; + const char *s; + + s = htsmsg_get_str(m, "bandwidth"); + if(s == NULL || (r = dvb_str2bw(s)) < 0) + return "Invalid bandwidth"; + dmc->dmc_fe_params.u.ofdm.bandwidth = r; + + s = htsmsg_get_str(m, "constellation"); + if(s == NULL || (r = dvb_str2qam(s)) < 0) + return "Invalid QAM constellation"; + dmc->dmc_fe_params.u.ofdm.constellation = r; + + s = htsmsg_get_str(m, "transmission_mode"); + if(s == NULL || (r = dvb_str2mode(s)) < 0) + return "Invalid transmission mode"; + dmc->dmc_fe_params.u.ofdm.transmission_mode = r; + + s = htsmsg_get_str(m, "guard_interval"); + if(s == NULL || (r = dvb_str2guard(s)) < 0) + return "Invalid guard interval"; + dmc->dmc_fe_params.u.ofdm.guard_interval = r; + + s = htsmsg_get_str(m, "hierarchy"); + if(s == NULL || (r = dvb_str2hier(s)) < 0) + return "Invalid heirarchy information"; + dmc->dmc_fe_params.u.ofdm.hierarchy_information = r; + + s = htsmsg_get_str(m, "fec_hi"); + if(s == NULL || (r = dvb_str2fec(s)) < 0) + return "Invalid hi-FEC"; + dmc->dmc_fe_params.u.ofdm.code_rate_HP = r; + + s = htsmsg_get_str(m, "fec_lo"); + if(s == NULL || (r = dvb_str2fec(s)) < 0) + return "Invalid lo-FEC"; + dmc->dmc_fe_params.u.ofdm.code_rate_LP = r; + + return NULL; +} + +static const char * +dvb_mux_conf_load_dvbc ( dvb_mux_conf_t *dmc, htsmsg_t *m ) +{ + int r; + const char *s; + + htsmsg_get_u32(m, "symbol_rate", &dmc->dmc_fe_params.u.qam.symbol_rate); + if(dmc->dmc_fe_params.u.qam.symbol_rate == 0) + return "Invalid symbol rate"; + + s = htsmsg_get_str(m, "constellation"); + if(s == NULL || (r = dvb_str2qam(s)) < 0) + return "Invalid QAM constellation"; + dmc->dmc_fe_params.u.qam.modulation = r; + + s = htsmsg_get_str(m, "fec"); + if(s == NULL || (r = dvb_str2fec(s)) < 0) + return "Invalid FEC"; + dmc->dmc_fe_params.u.qam.fec_inner = r; + + return NULL; +} + +static const char * +dvb_mux_conf_load_dvbs ( dvb_mux_conf_t *dmc, htsmsg_t *m ) +{ + int r; + const char *s; + + htsmsg_get_u32(m, "symbol_rate", &dmc->dmc_fe_params.u.qpsk.symbol_rate); + if(dmc->dmc_fe_params.u.qpsk.symbol_rate == 0) + return "Invalid symbol rate"; + + s = htsmsg_get_str(m, "fec"); + if(s == NULL || (r = dvb_str2fec(s)) < 0) + return "Invalid FEC"; + dmc->dmc_fe_params.u.qpsk.fec_inner = r; + + s = htsmsg_get_str(m, "polarisation"); + if(s == NULL || (r = dvb_str2pol(s)) < 0) + return "Invalid polarisation"; + dmc->dmc_fe_polarisation = r; + +#if DVB_API_VERSION >= 5 + s = htsmsg_get_str(m, "modulation"); + if(s == NULL || (r = dvb_str2qam(s)) < 0) { + r = QPSK; + tvhlog(LOG_INFO, "dvb", "no modulation, using default QPSK"); + } + dmc->dmc_fe_modulation = r; + + s = htsmsg_get_str(m, "rolloff"); + if(s == NULL || (r = dvb_str2rolloff(s)) < 0) { + r = ROLLOFF_35; + tvhlog(LOG_INFO, "dvb", "no rolloff, using default ROLLOFF_35"); + } + dmc->dmc_fe_rolloff = r; + + // TODO: pilot mode +#endif + return NULL; +} + +static const char * +dvb_mux_conf_load_atsc ( dvb_mux_conf_t *dmc, htsmsg_t *m ) +{ + int r; + const char *s; + s = htsmsg_get_str(m, "constellation"); + if(s == NULL || (r = dvb_str2qam(s)) < 0) + return "Invalid VSB constellation"; + dmc->dmc_fe_params.u.vsb.modulation = r; + return NULL; +} + +const char * +dvb_mux_conf_load ( fe_type_t type, dvb_mux_conf_t *dmc, htsmsg_t *m ) +{ + int r; + uint32_t u32; + const char *str; + + memset(dmc, 0, sizeof(dvb_mux_conf_t)); + dmc->dmc_fe_params.inversion = INVERSION_AUTO; + + /* Delivery system */ +#if DVB_API_VERSION >= 5 + str = htsmsg_get_str(m, "delsys"); + if (!str || (r = dvb_str2delsys(str)) < 0) { + if (type == FE_OFDM) r = SYS_DVBT; + else if (type == FE_QAM) r = SYS_DVBC_ANNEX_B; + else if (type == FE_QPSK) r = SYS_DVBS; + else if (type == FE_ATSC) r = SYS_ATSC; + else + return "Invalid FE type"; + tvhlog(LOG_INFO, "dvb", "no delsys, using default %s", dvb_delsys2str(r)); + } +#endif + dmc->dmc_fe_delsys = r; + + /* Frequency */ + if (htsmsg_get_u32(m, "frequency", &u32)) + return "Invalid frequency"; + dmc->dmc_fe_params.frequency = u32; + + /* Type specific */ + if (type == FE_OFDM) return dvb_mux_conf_load_dvbt(dmc, m); + else if (type == FE_QAM) return dvb_mux_conf_load_dvbc(dmc, m); + else if (type == FE_QPSK) return dvb_mux_conf_load_dvbs(dmc, m); + else if (type == FE_ATSC) return dvb_mux_conf_load_atsc(dmc, m); + else + return "Invalid FE type"; +} + #endif /* ENABLE_DVBAPI */ diff --git a/src/input/mpegts/linuxdvb/linuxdvb_device.c b/src/input/mpegts/linuxdvb/linuxdvb_device.c index e94c80ea..75478834 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_device.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_device.c @@ -249,7 +249,7 @@ linuxdvb_device_create0 ( const char *uuid, htsmsg_t *conf ) /* Load config */ linuxdvb_hardware_load((linuxdvb_hardware_t*)ld, conf); if (!htsmsg_get_u32(conf, "enabled", &u32) && u32) - ld->lh_enabled = 1; + ld->mi_enabled = 1; if ((str = htsmsg_get_str(conf, "displayname"))) ld->lh_displayname = strdup(str); if ((str = htsmsg_get_str(conf, "devid"))) diff --git a/src/input/mpegts/linuxdvb/linuxdvb_frontend.c b/src/input/mpegts/linuxdvb/linuxdvb_frontend.c index dee1a793..4181a1f3 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_frontend.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_frontend.c @@ -158,6 +158,15 @@ static void linuxdvb_frontend_stop_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) { + linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi; + + /* Stop thread */ + if (lfe->lfe_dvr_pipe.wr > 0) { + tvh_write(lfe->lfe_dvr_pipe.wr, "", 1); + pthread_join(lfe->lfe_dvr_thread, NULL); + tvh_pipe_close(&lfe->lfe_dvr_pipe); + tvhlog(LOG_DEBUG, "linuxdvb", "stopped dvr thread"); + } } static void * @@ -212,6 +221,9 @@ linuxdvb_frontend_input_thread ( void *aux ) ev.events = EPOLLIN; ev.data.fd = dvr; epoll_ctl(efd, EPOLL_CTL_ADD, ev.data.fd, &ev); + ev.events = EPOLLIN; + ev.data.fd = lfe->lfe_dvr_pipe.rd; + epoll_ctl(efd, EPOLL_CTL_ADD, ev.data.fd, &ev); /* Read */ while (1) { @@ -250,7 +262,6 @@ linuxdvb_frontend_monitor ( void *aux ) linuxdvb_frontend_t *lfe = aux; mpegts_mux_instance_t *mmi = LIST_FIRST(&lfe->mi_mux_active); mpegts_mux_t *mm; - pthread_t tid; if (!mmi) return; mm = mmi->mmi_mux; @@ -265,7 +276,10 @@ linuxdvb_frontend_monitor ( void *aux ) if (fe_status & FE_HAS_LOCK) { // Note: the lock // Open pending services - pthread_create(&tid, NULL, linuxdvb_frontend_input_thread, lfe); + tvh_pipe(O_NONBLOCK, &lfe->lfe_dvr_pipe); + pthread_create(&lfe->lfe_dvr_thread, NULL, linuxdvb_frontend_input_thread, lfe); + + // TODO: these tables need to vary based on type mpegts_table_add(mm, DVB_PAT_BASE, DVB_PAT_MASK, dvb_pat_callback, NULL, "pat", MT_QUICKREQ | MT_CRC, DVB_PAT_PID); mpegts_table_add(mm, DVB_SDT_BASE, DVB_SDT_MASK, dvb_sdt_callback, @@ -460,6 +474,9 @@ linuxdvb_frontend_create0 if (!conf) return lfe; + if (!htsmsg_get_u32(conf, "enabled", &u32) && u32) + lfe->mi_enabled = u32; + printf("LFE ENABLED = %d\n", lfe->mi_enabled); if (!htsmsg_get_u32(conf, "number", &u32)) lfe->lfe_number = u32; if ((str = htsmsg_get_str(conf, "network"))) { diff --git a/src/input/mpegts/linuxdvb/linuxdvb_hardware.c b/src/input/mpegts/linuxdvb/linuxdvb_hardware.c index 0c62c5c1..053d9540 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_hardware.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_hardware.c @@ -47,7 +47,7 @@ linuxdvb_hardware_enumerate ( linuxdvb_hardware_list_t *list ) void linuxdvb_hardware_save ( linuxdvb_hardware_t *lh, htsmsg_t *m ) { - htsmsg_add_u32(m, "enabled", lh->lh_enabled); + htsmsg_add_u32(m, "enabled", lh->mi_enabled); if (lh->lh_displayname) htsmsg_add_str(m, "displayname", lh->lh_displayname); } @@ -57,7 +57,7 @@ void linuxdvb_hardware_load ( linuxdvb_hardware_t *lh, htsmsg_t *conf ) uint32_t u32; const char *str; if (!htsmsg_get_u32(conf, "enabled", &u32) && u32) - lh->lh_enabled = 1; + lh->mi_enabled = 1; if ((str = htsmsg_get_str(conf, "displayname"))) lh->lh_displayname = strdup(str); } @@ -82,7 +82,7 @@ const idclass_t linuxdvb_hardware_class = .ic_get_childs = linuxdvb_hardware_class_get_childs, .ic_properties = (const property_t[]){ { PROPDEF1("enabled", "Enabled", - PT_BOOL, linuxdvb_hardware_t, lh_enabled) }, + PT_BOOL, linuxdvb_hardware_t, mi_enabled) }, { PROPDEF1("displayname", "Name", PT_STR, linuxdvb_hardware_t, lh_displayname) }, {} diff --git a/src/input/mpegts/linuxdvb/linuxdvb_mux.c b/src/input/mpegts/linuxdvb/linuxdvb_mux.c index 2bdbe974..3347f460 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_mux.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_mux.c @@ -93,31 +93,32 @@ linuxdvb_mux_config_save ( mpegts_mux_t *mm ) { } -#if 0 -static int -linuxdvb_mux_start ( mpegts_mux_t *mm, const char *reason, int weight ) -{ - return SM_CODE_TUNING_FAILED; -} - static void -linuxdvb_mux_stop ( mpegts_mux_t *mm ) +linuxdvb_mux_display_name ( mpegts_mux_t *mm, char *buf, size_t len ) { + size_t c = 0; + const char *unit = "Hz"; + linuxdvb_mux_t *lm = (linuxdvb_mux_t*)mm; + c = snprintf(buf+c, len-c, "%d %s [%04X:%04X]", + lm->lm_tuning.dmc_fe_params.frequency, + unit, mm->mm_onid, mm->mm_tsid); } -#endif - -extern const idclass_t mpegts_mux_instance_class; static void linuxdvb_mux_create_instances ( mpegts_mux_t *mm ) { + extern const idclass_t mpegts_mux_instance_class; mpegts_input_t *mi; mpegts_mux_instance_t *mmi; + tvhtrace("linuxdvb", "mm %p create instances", mm); LIST_FOREACH(mi, &mm->mm_network->mn_inputs, mi_network_link) { + tvhtrace("linuxdvb", " checking mi %p", mi); LIST_FOREACH(mmi, &mi->mi_mux_instances, mmi_input_link) if (mmi->mmi_mux == mm) break; - if (!mmi) + if (!mmi) { mmi = mpegts_mux_instance_create(mpegts_mux_instance, NULL, mi, mm); + tvhtrace("linuxdvb", " created mmi %p", mmi); + } // TODO: we might eventually want to keep history! } } @@ -134,57 +135,6 @@ linuxdvb_mux_close_table ( mpegts_mux_t *mm, mpegts_table_t *mt ) } #endif -static const char * -dvb_mux_conf_load ( fe_type_t type, dvb_mux_conf_t *dmc, htsmsg_t *m ) -{ - int r; - const char *s; - dmc->dmc_fe_params.inversion = INVERSION_AUTO; - htsmsg_get_u32(m, "frequency", &dmc->dmc_fe_params.frequency); - - switch(type) { - case FE_OFDM: - s = htsmsg_get_str(m, "bandwidth"); - if(s == NULL || (r = dvb_str2bw(s)) < 0) - return "Invalid bandwidth"; - dmc->dmc_fe_params.u.ofdm.bandwidth = r; - - s = htsmsg_get_str(m, "constellation"); - if(s == NULL || (r = dvb_str2qam(s)) < 0) - return "Invalid QAM constellation"; - dmc->dmc_fe_params.u.ofdm.constellation = r; - - s = htsmsg_get_str(m, "transmission_mode"); - if(s == NULL || (r = dvb_str2mode(s)) < 0) - return "Invalid transmission mode"; - dmc->dmc_fe_params.u.ofdm.transmission_mode = r; - - s = htsmsg_get_str(m, "guard_interval"); - if(s == NULL || (r = dvb_str2guard(s)) < 0) - return "Invalid guard interval"; - dmc->dmc_fe_params.u.ofdm.guard_interval = r; - - s = htsmsg_get_str(m, "hierarchy"); - if(s == NULL || (r = dvb_str2hier(s)) < 0) - return "Invalid heirarchy information"; - dmc->dmc_fe_params.u.ofdm.hierarchy_information = r; - - s = htsmsg_get_str(m, "fec_hi"); - if(s == NULL || (r = dvb_str2fec(s)) < 0) - return "Invalid hi-FEC"; - dmc->dmc_fe_params.u.ofdm.code_rate_HP = r; - - s = htsmsg_get_str(m, "fec_lo"); - if(s == NULL || (r = dvb_str2fec(s)) < 0) - return "Invalid lo-FEC"; - dmc->dmc_fe_params.u.ofdm.code_rate_LP = r; - break; - default: - return "Not yet supported"; - } - return NULL; -} - linuxdvb_mux_t * linuxdvb_mux_create0 ( linuxdvb_network_t *ln, @@ -193,6 +143,7 @@ linuxdvb_mux_create0 { const idclass_t *idc; mpegts_mux_t *mm; + linuxdvb_mux_t *lm; /* Search for existing */ @@ -214,7 +165,13 @@ linuxdvb_mux_create0 if (!(mm = mpegts_mux_create0(calloc(1, sizeof(linuxdvb_mux_t)), idc, uuid, (mpegts_network_t*)ln, onid, tsid))) return NULL; - memcpy(&((linuxdvb_mux_t*)mm)->lm_tuning, dmc, sizeof(dvb_mux_conf_t)); + lm = (linuxdvb_mux_t*)mm; + memcpy(&lm->lm_tuning, dmc, sizeof(dvb_mux_conf_t)); + + /* Callbacks */ + lm->mm_display_name = linuxdvb_mux_display_name; + lm->mm_config_save = linuxdvb_mux_config_save; + lm->mm_create_instances = linuxdvb_mux_create_instances; return (linuxdvb_mux_t*)mm; } @@ -244,14 +201,6 @@ linuxdvb_mux_create1 if (!lm) printf("OH DEAR\n"); if (!lm) return NULL; - /* Callbacks */ - lm->mm_config_save = linuxdvb_mux_config_save; -#if 0 - lm->mm_open_table = linuxdvb_mux_open_table; - lm->mm_close_table = linuxdvb_mux_close_table; -#endif - lm->mm_create_instances = linuxdvb_mux_create_instances; - /* No config */ if (!conf) return lm; diff --git a/src/input/mpegts/linuxdvb/linuxdvb_network.c b/src/input/mpegts/linuxdvb/linuxdvb_network.c index 1079193e..5ca94fdd 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_network.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_network.c @@ -46,12 +46,29 @@ const idclass_t linuxdvb_network_class = } }; +static mpegts_mux_t * +linuxdvb_network_find_mux + ( linuxdvb_network_t *ln, dvb_mux_conf_t *dmc ) +{ + mpegts_mux_t *mm; + LIST_FOREACH(mm, &ln->mn_muxes, mm_network_link) { + linuxdvb_mux_t *lm = (linuxdvb_mux_t*)mm; + if (abs(lm->lm_tuning.dmc_fe_params.frequency - dmc->dmc_fe_params.frequency) > 2000) continue; + if (lm->lm_tuning.dmc_fe_polarisation != dmc->dmc_fe_polarisation) continue; + break; + } + return mm; +} + static mpegts_mux_t * linuxdvb_network_create_mux ( mpegts_mux_t *mm, uint16_t onid, uint16_t tsid, dvb_mux_conf_t *conf ) { linuxdvb_network_t *ln = (linuxdvb_network_t*)mm->mm_network; - return (mpegts_mux_t*)linuxdvb_mux_create0(ln, onid, tsid, conf, NULL); + mm = linuxdvb_network_find_mux(ln, conf); + if (!mm) + mm = (mpegts_mux_t*)linuxdvb_mux_create0(ln, onid, tsid, conf, NULL); + return mm; } static mpegts_service_t * diff --git a/src/input/mpegts/linuxdvb/linuxdvb_private.h b/src/input/mpegts/linuxdvb/linuxdvb_private.h index 5e2a80d0..540ed4fd 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_private.h +++ b/src/input/mpegts/linuxdvb/linuxdvb_private.h @@ -62,7 +62,6 @@ struct linuxdvb_hardware /* * Device info */ - int lh_enabled; char *lh_displayname; }; @@ -132,8 +131,13 @@ struct linuxdvb_frontend char *lfe_dmx_path; char *lfe_dvr_path; + /* + * Reception + */ int lfe_fe_fd; int lfe_dvr_fd; + pthread_t lfe_dvr_thread; + th_pipe_t lfe_dvr_pipe; /* * Tuning diff --git a/src/input/mpegts/mpegts_input.c b/src/input/mpegts/mpegts_input.c index ae0d587e..fb6a1a5d 100644 --- a/src/input/mpegts/mpegts_input.c +++ b/src/input/mpegts/mpegts_input.c @@ -185,6 +185,7 @@ int mpegts_input_is_free ( mpegts_input_t *mi ) { mpegts_mux_instance_t *mmi = LIST_FIRST(&mi->mi_mux_active); + tvhtrace("mpegts", "input_is_free(%p) mmi = %p", mi, mmi); return mmi ? 0 : 1; } @@ -204,6 +205,12 @@ mpegts_input_current_weight ( mpegts_input_t *mi ) return w; } +static int +mpegts_input_is_enabled ( mpegts_input_t *mi ) +{ + return mi->mi_enabled; +} + mpegts_input_t* mpegts_input_create0 ( mpegts_input_t *mi, const idclass_t *class, const char *uuid ) @@ -215,6 +222,7 @@ mpegts_input_create0 mi->mi_stop_mux = NULL; mi->mi_open_service = mpegts_input_open_service; mi->mi_close_service = mpegts_input_close_service; + mi->mi_is_enabled = mpegts_input_is_enabled; mi->mi_is_free = mpegts_input_is_free; mi->mi_current_weight = mpegts_input_current_weight; diff --git a/src/input/mpegts/mpegts_mux.c b/src/input/mpegts/mpegts_mux.c index 06473114..9040b3f5 100644 --- a/src/input/mpegts/mpegts_mux.c +++ b/src/input/mpegts/mpegts_mux.c @@ -116,6 +116,19 @@ mpegts_mux_initial_scan_timeout ( void *aux ) mpegts_mux_initial_scan_done(mm); } +static int +mpegts_mux_has_subscribers ( mpegts_mux_t *mm ) +{ + service_t *t; + mpegts_mux_instance_t *mmi = mm->mm_active; + if (mmi) { + LIST_FOREACH(t, &mmi->mmi_input->mi_transports, s_active_link) + if (((mpegts_service_t*)t)->s_dvb_mux == mm) + return 1; + } + return 0; +} + void mpegts_mux_initial_scan_done ( mpegts_mux_t *mm ) { @@ -127,12 +140,18 @@ mpegts_mux_initial_scan_done ( mpegts_mux_t *mm ) mm->mm_initial_scan_status = MM_SCAN_DONE; TAILQ_REMOVE(&mn->mn_initial_scan_current_queue, mm, mm_initial_scan_link); mpegts_network_schedule_initial_scan(mn); + + /* Stop */ + if (!mpegts_mux_has_subscribers(mm)) + mm->mm_stop(mm); + // TODO: save } static int mpegts_mux_start ( mpegts_mux_t *mm, const char *reason, int weight ) { + char buf[128]; mpegts_network_t *mn = mm->mm_network; mpegts_mux_instance_t *mmi; @@ -147,6 +166,8 @@ mpegts_mux_start ( mpegts_mux_t *mm, const char *reason, int weight ) /* Create mux instances (where needed) */ mm->mm_create_instances(mm); + if (!LIST_FIRST(&mm->mm_instances)) + tvhtrace("mpegts", "mm %p has no instances", mm); /* Find */ // TODO: don't like this is unbounded, if for some reason mi_start_mux() @@ -154,10 +175,12 @@ mpegts_mux_start ( mpegts_mux_t *mm, const char *reason, int weight ) while (1) { /* Find free input */ - LIST_FOREACH(mmi, &mm->mm_instances, mmi_mux_link) + LIST_FOREACH(mmi, &mm->mm_instances, mmi_mux_link) { if (!mmi->mmi_tune_failed && + mmi->mmi_input->mi_is_enabled(mmi->mmi_input) && mmi->mmi_input->mi_is_free(mmi->mmi_input)) break; + } if (mmi) tvhtrace("mpegts", "found free mmi %p", mmi); else @@ -189,6 +212,8 @@ mpegts_mux_start ( mpegts_mux_t *mm, const char *reason, int weight ) } /* Tune */ + mm->mm_display_name(mm, buf, sizeof(buf)); + tvhlog(LOG_INFO, "mpegts", "tuning %s", buf); if (!mmi->mmi_input->mi_start_mux(mmi->mmi_input, mmi)) { LIST_INSERT_HEAD(&mmi->mmi_input->mi_mux_active, mmi, mmi_active_link); mm->mm_active = mmi; @@ -203,7 +228,7 @@ mpegts_mux_start ( mpegts_mux_t *mm, const char *reason, int weight ) TAILQ_REMOVE(&mn->mn_initial_scan_pending_queue, mm, mm_initial_scan_link); mm->mm_initial_scan_status = MM_SCAN_CURRENT; TAILQ_INSERT_TAIL(&mn->mn_initial_scan_current_queue, mm, mm_initial_scan_link); - gtimer_arm(&mm->mm_initial_scan_timeout, mpegts_mux_initial_scan_timeout, mm, 10); + gtimer_arm(&mm->mm_initial_scan_timeout, mpegts_mux_initial_scan_timeout, mm, 30); } return 0; @@ -217,11 +242,15 @@ mpegts_mux_stop ( mpegts_mux_t *mm ) mpegts_input_t *mi; tvhtrace("mpegts", "stopping mux %p", mm); - assert(0); - /* Flush all subscribers */ if (mmi) { mi = mmi->mmi_input; + mi->mi_stop_mux(mi, mmi); + mm->mm_active = NULL; + LIST_REMOVE(mmi, mmi_active_link); + tvhtrace("mpegts", "active first = %p", LIST_FIRST(&mi->mi_mux_active)); + + /* Flush all subscribers */ s = LIST_FIRST(&mi->mi_transports); while (s) { t = s; diff --git a/src/input/mpegts/mpegts_table.c b/src/input/mpegts/mpegts_table.c index 7efc79fd..05f3ff28 100644 --- a/src/input/mpegts/mpegts_table.c +++ b/src/input/mpegts/mpegts_table.c @@ -29,9 +29,10 @@ mpegts_table_fastswitch ( mpegts_mux_t *mm ) if(mm->mm_initial_scan_status == MM_SCAN_DONE) return; - LIST_FOREACH(mt, &mm->mm_tables, mt_link) + LIST_FOREACH(mt, &mm->mm_tables, mt_link) { if((mt->mt_flags & MT_QUICKREQ) && mt->mt_count == 0) return; + } // TODO: //dvb_mux_save(dm); @@ -121,11 +122,12 @@ mpegts_table_add mpegts_table_t *mt; /* Check for existing */ - LIST_FOREACH(mt, &mm->mm_tables, mt_link) + LIST_FOREACH(mt, &mm->mm_tables, mt_link) { if ( mt->mt_pid == pid && mt->mt_callback == callback && mt->mt_opaque == opaque ) return; + } tvhtrace("mpegts", "add %s table %02X/%02X (%d) pid %04X (%d)", name, tableid, mask, tableid, pid, pid);