diff --git a/Makefile b/Makefile index 926e1e90..2c8178e9 100644 --- a/Makefile +++ b/Makefile @@ -76,8 +76,9 @@ SRCS = src/main.c \ SRCS += src/epggrab/pyepg.c\ src/epggrab/xmltv.c\ + src/epggrab/ota.c \ src/epggrab/eit.c \ - src/epggrab/opentv.c + src/epggrab/opentv.c \ SRCS += src/plumbing/tsfix.c \ src/plumbing/globalheaders.c \ diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 8f2774b5..80564a4a 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -120,6 +120,8 @@ typedef struct th_dvb_mux_instance { int tdmi_enabled; + LIST_HEAD(, epggrab_ota_mux) tdmi_epg_grabbers; + time_t tdmi_got_adapter; time_t tdmi_lost_adapter; @@ -148,13 +150,18 @@ typedef struct th_dvb_mux_instance { */ #define TDA_MUX_HASH_WIDTH 101 +#define TDA_SCANQ_BAD 0 ///< Bad muxes (monitor quality) +#define TDA_SCANQ_OK 1 ///< OK muxes +#define TDA_SCANQ_EPG 2 ///< EPG muxes (TBD) +#define TDA_SCANQ_NUM 3 + typedef struct th_dvb_adapter { TAILQ_ENTRY(th_dvb_adapter) tda_global_link; struct th_dvb_mux_instance_list tda_muxes; - struct th_dvb_mux_instance_queue tda_scan_queues[2]; + struct th_dvb_mux_instance_queue tda_scan_queues[TDA_SCANQ_NUM]; int tda_scan_selector; struct th_dvb_mux_instance_queue tda_initial_scan_queue; diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 0337aa9d..12e13066 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -41,6 +41,7 @@ #include "tsdemux.h" #include "notify.h" #include "service.h" +#include "epggrab/ota.h" struct th_dvb_adapter_queue dvb_adapters; struct th_dvb_mux_instance_tree dvb_muxes; @@ -53,11 +54,12 @@ static void *dvb_adapter_input_dvr(void *aux); static th_dvb_adapter_t * tda_alloc(void) { + int i; th_dvb_adapter_t *tda = calloc(1, sizeof(th_dvb_adapter_t)); pthread_mutex_init(&tda->tda_delivery_mutex, NULL); - TAILQ_INIT(&tda->tda_scan_queues[0]); - TAILQ_INIT(&tda->tda_scan_queues[1]); + for (i = 0; i < TDA_SCANQ_NUM; i++ ) + TAILQ_INIT(&tda->tda_scan_queues[i]); TAILQ_INIT(&tda->tda_initial_scan_queue); TAILQ_INIT(&tda->tda_satconfs); @@ -418,13 +420,21 @@ dvb_adapter_init(uint32_t adapter_mask) void dvb_adapter_mux_scanner(void *aux) { + epggrab_ota_mux_t *ota; th_dvb_adapter_t *tda = aux; th_dvb_mux_instance_t *tdmi; int i; + int idle_epg; + static const char* scan_string[] = { + "Autoscan BAD", + "Autoscan OK", + "Autoscan EPG" + }; if(tda->tda_rootpath == NULL) return; // No hardware + // default period gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 20); if(LIST_FIRST(&tda->tda_muxes) == NULL) @@ -439,8 +449,18 @@ dvb_adapter_mux_scanner(void *aux) return; } - if(!tda->tda_idlescan && TAILQ_FIRST(&tda->tda_scan_queues[0]) == NULL) { - /* Idlescan is disabled and no muxes are bad */ + /* Mark any incomplete EPG grabbers as timed out (basically complete) */ + if (tda->tda_mux_current) + LIST_FOREACH(ota, &tda->tda_mux_current->tdmi_epg_grabbers, tdmi_link) { + epggrab_ota_timeout(ota); + } + + /* Idle or EPG scan enabled */ + idle_epg = tda->tda_idlescan || + TAILQ_FIRST(&tda->tda_scan_queues[TDA_SCANQ_EPG]); + + /* Idlescan is disabled and no muxes are bad */ + if(!idle_epg && TAILQ_FIRST(&tda->tda_scan_queues[TDA_SCANQ_BAD]) == NULL) { if(!tda->tda_qmon) return; // Quality monitoring is disabled @@ -452,12 +472,26 @@ dvb_adapter_mux_scanner(void *aux) return; } - /* Alternate between the other two (bad and OK) */ - for(i = 0; i < 2; i++) { - tda->tda_scan_selector = !tda->tda_scan_selector; + /* Alternate between the three queues (BAD, OK, EPG) */ + for(i = 0; i < TDA_SCANQ_NUM; i++) { + tda->tda_scan_selector++; + if (tda->tda_scan_selector == TDA_SCANQ_NUM) + tda->tda_scan_selector = 0; + tdmi = TAILQ_FIRST(&tda->tda_scan_queues[tda->tda_scan_selector]); if(tdmi != NULL) { - dvb_fe_tune(tdmi, "Autoscan"); + + /* EPG - adjust dwell time */ + if (tda->tda_scan_selector == TDA_SCANQ_EPG) { + epggrab_ota_mux_t *ota; + int period = 20; + LIST_FOREACH(ota, &tdmi->tdmi_epg_grabbers, tdmi_link) { + if (ota->timeout > period) period = ota->timeout; + } + gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, period); + } + + dvb_fe_tune(tdmi, scan_string[tda->tda_scan_selector]); return; } } diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index bb7d2249..b8329f76 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -228,7 +228,17 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi) assert(tdmi->tdmi_scan_queue == NULL); if(tdmi->tdmi_enabled) { - tdmi->tdmi_scan_queue = &tda->tda_scan_queues[tdmi->tdmi_quality == 100]; + int ti; + time_t now; + epggrab_ota_mux_t *ota; + time(&now); + LIST_FOREACH(ota, &tdmi->tdmi_epg_grabbers, tdmi_link) { + if ( now >= (ota->completed+ota->interval) ) break; + } + ti = ota ? TDA_SCANQ_EPG + : tdmi->tdmi_quality == 100 ? TDA_SCANQ_OK + : TDA_SCANQ_BAD; + tdmi->tdmi_scan_queue = &tda->tda_scan_queues[ti]; TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); } diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index f311abdf..31f83093 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -44,6 +44,7 @@ #include "dvb_support.h" #include "notify.h" #include "subscriptions.h" +#include "epggrab/ota.h" struct th_dvb_mux_instance_tree dvb_muxes; @@ -297,6 +298,7 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, void dvb_mux_destroy(th_dvb_mux_instance_t *tdmi) { + epggrab_ota_mux_t *ota; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; service_t *t; @@ -330,6 +332,9 @@ dvb_mux_destroy(th_dvb_mux_instance_t *tdmi) if(tdmi->tdmi_table_initial) tda->tda_initial_num_mux--; + while ((ota = LIST_FIRST(&tdmi->tdmi_epg_grabbers))) + epggrab_ota_unregister(ota); + hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier); free(tdmi->tdmi_network); diff --git a/src/epggrab.c b/src/epggrab.c index 62948eae..51c4fb5c 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -10,7 +10,7 @@ #include "htsmsg.h" #include "settings.h" #include "tvheadend.h" -#include "src/queue.h" +#include "queue.h" #include "epg.h" #include "epggrab.h" #include "epggrab/eit.h" @@ -834,6 +834,18 @@ void epggrab_channel_mod ( channel_t *ch ) void epggrab_tune ( th_dvb_mux_instance_t *tdmi ) { epggrab_module_t *m; + epggrab_ota_mux_t *o; + th_dvb_mux_instance_t *t; + + /* Cancel all currently active ota grabs */ + LIST_FOREACH(t, &tdmi->tdmi_adapter->tda_muxes, tdmi_adapter_link) { + if (t == tdmi) continue; + LIST_FOREACH(o, &t->tdmi_epg_grabbers, tdmi_link) { + epggrab_ota_cancel(o); + } + } + + /* Inform all modules */ LIST_FOREACH(m, &epggrab_modules, link) { if (m->tune) m->tune(m, tdmi); } diff --git a/src/epggrab.h b/src/epggrab.h index 59a35897..8eebf029 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -1,11 +1,12 @@ #ifndef __EPGGRAB_H__ #define __EPGGRAB_H__ +typedef struct epggrab_module epggrab_module_t; + #include #include "channels.h" #include "dvb/dvb.h" - -typedef struct epggrab_module epggrab_module_t; +#include "epggrab/ota.h" /* ************************************************************************** * Grabber Stats @@ -96,13 +97,14 @@ struct epggrab_module { LIST_ENTRY(epggrab_module) link; ///< Global list link - const char *id; ///< Module identifier - const char *name; ///< Module name (for display) - const char *path; ///< Module path (for fixed config) - const uint8_t flags; ///< Mode flag - uint8_t enabled; ///< Whether the module is enabled - int sock; ///< Socket descriptor - epggrab_channel_tree_t *channels; ///< Channel list + const char *id; ///< Module identifier + const char *name; ///< Module name (for display) + const char *path; ///< Module path (for fixed config) + const uint8_t flags; ///< Mode flag + uint8_t enabled; ///< Whether the module is enabled + int sock; ///< Socket descriptor + epggrab_channel_tree_t *channels; ///< Channel list + LIST_HEAD(, epggrab_ota_mux) muxes; ///< Linked muxes /* Enable/Disable */ int (*enable) ( epggrab_module_t *m, uint8_t e ); diff --git a/src/epggrab/opentv.c b/src/epggrab/opentv.c index a423289e..0042afd9 100644 --- a/src/epggrab/opentv.c +++ b/src/epggrab/opentv.c @@ -25,6 +25,7 @@ #include "channels.h" #include "huffman.h" #include "epg.h" +#include "epggrab/ota.h" #include "epggrab/opentv.h" #include "subscriptions.h" #include "streaming.h" @@ -42,6 +43,9 @@ * dicts/provs etc... they're only used to get things up and running * ***********************************************************************/ +#define OPENTV_SCAN_MAX 600 // 10min max scan period +#define OPENTV_SCAN_PER 3600 // 1hour interval + /* Huffman dictionary */ typedef struct opentv_dict { @@ -378,10 +382,23 @@ static int _opentv_parse_event_section return 0; } +static void _opentv_table_callback + ( opentv_module_t *m, th_dvb_mux_instance_t *tdmi ) +{ + epggrab_ota_mux_t *ota; + ota = epggrab_ota_register((epggrab_module_t*)m, tdmi, + OPENTV_SCAN_MAX, OPENTV_SCAN_PER); + if (epggrab_ota_begin(ota)) { + // setup internal status + } +} + static int _opentv_event_callback ( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p ) { - return _opentv_parse_event_section((opentv_module_t*)p, buf, len); + opentv_module_t *mod = (opentv_module_t*)p; + _opentv_table_callback(mod, tdmi); + return _opentv_parse_event_section(mod, buf, len); } // TODO: this function is currently a bit of a mess @@ -395,7 +412,7 @@ static int _opentv_channel_callback uint16_t sid; int i, j, k, tdlen, dlen, dtag, tslen; channel_t *ch; - if (tid != 0x4a) return 0; + _opentv_table_callback(mod, tdmi); i = 7 + ((((int)buf[5] & 0xf) << 8) | buf[6]); tslen = (((int)buf[i] & 0xf) << 8) | buf[i+1]; i += 2; @@ -441,82 +458,6 @@ static int _opentv_channel_callback return 0; } -/* ************************************************************************ - * Tuning Thread - * ***********************************************************************/ - -static void _opentv_stream ( void *p, streaming_message_t *sm ) -{ - // TODO: handle start? - // TODO: handle stop? -} - -// TODO: if channel not found we still wait -// TODO: make time periods configurable? -// TODO: dynamic detection of when to start/stop -static void* _opentv_thread ( void *p ) -{ - int err; - service_t *svc; - th_subscription_t *sub; - streaming_target_t stream; - time_t start; - struct timespec ts; - opentv_module_t *mod = (opentv_module_t*)p; - streaming_target_init(&stream, _opentv_stream, NULL, 0); - tvhlog(LOG_INFO, mod->id, "thread started\n"); - - do { - - /* Find channel and subscribe */ - tvhlog(LOG_DEBUG, mod->id, "grab begin %d %d", mod->prov->tsid, mod->prov->sid); - pthread_mutex_lock(&global_lock); - svc = _opentv_find_service(mod->prov->tsid, mod->prov->sid); - if (svc) { - sub = subscription_create_from_service(svc, mod->prov->id, - &stream, 0); - if (sub) subscription_change_weight(sub, 1); - } - else - sub = NULL; - pthread_mutex_unlock(&global_lock); - if (sub) tvhlog(LOG_DEBUG, mod->id, "subscription added"); - - /* Allow scanning */ - if (sub) { - time(&start); - ts.tv_nsec = 0; - pthread_mutex_lock(&mod->mutex); - while ( mod->enabled ) { - ts.tv_sec = start + 300; - err = pthread_cond_timedwait(&mod->cond, &mod->mutex, &ts); - if (err == ETIMEDOUT ) break; - } - pthread_mutex_unlock(&mod->mutex); - } - tvhlog(LOG_DEBUG, mod->id, "grab complete"); - - /* Terminate subscription */ - pthread_mutex_lock(&global_lock); - if (sub) subscription_unsubscribe(sub); - pthread_mutex_unlock(&global_lock); - - /* Wait */ - time(&mod->updated); - pthread_mutex_lock(&mod->mutex); - while ( mod->enabled ) { - ts.tv_sec = mod->updated + 3600; - err = pthread_cond_timedwait(&mod->cond, &mod->mutex, &ts); - if (err == ETIMEDOUT) break; - } - if (!mod->enabled) break; - pthread_mutex_unlock(&mod->mutex); - } while (1); - pthread_mutex_unlock(&mod->mutex); - - return NULL; -} - /* ************************************************************************ * Module Setup * ***********************************************************************/ @@ -528,7 +469,7 @@ static void _opentv_tune ( epggrab_module_t *m, th_dvb_mux_instance_t *tdmi ) int *t; struct dmx_sct_filter_params *fp; opentv_module_t *mod = (opentv_module_t*)m; - + /* Install tables */ if (m->enabled && (mod->prov->tsid == tdmi->tdmi_transport_stream_id)) { tvhlog(LOG_INFO, "opentv", "install provider %s tables", mod->prov->id); @@ -568,23 +509,27 @@ static void _opentv_tune ( epggrab_module_t *m, th_dvb_mux_instance_t *tdmi ) static int _opentv_enable ( epggrab_module_t *m, uint8_t e ) { - int save = 0; - pthread_t t; - pthread_attr_t ta; + th_dvb_adapter_t *tda; + th_dvb_mux_instance_t *tdmi; opentv_module_t *mod = (opentv_module_t*)m; - pthread_mutex_lock(&mod->mutex); - if (m->enabled != e) { - m->enabled = e; - if (e) { - pthread_attr_init(&ta); - pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED); - pthread_create(&t, &ta, _opentv_thread, mod); - } else - pthread_cond_signal(&mod->cond); - save = 1; + + if (m->enabled == e) return 0; + + m->enabled = e; + + /* Find muxes and enable/disable */ + TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { + LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) { + if (tdmi->tdmi_transport_stream_id != mod->prov->tsid) continue; + if (e) { + epggrab_ota_register(m, tdmi, OPENTV_SCAN_MAX, OPENTV_SCAN_PER); + } else { + epggrab_ota_unregister_one(m, tdmi); + } + } } - pthread_mutex_unlock(&mod->mutex); - return save; + + return 1; } void opentv_init ( epggrab_module_list_t *list ) diff --git a/src/epggrab/ota.c b/src/epggrab/ota.c new file mode 100644 index 00000000..4c3f4393 --- /dev/null +++ b/src/epggrab/ota.c @@ -0,0 +1,110 @@ +/* + * Electronic Program Guide - EPG grabber OTA functions + * Copyright (C) 2012 Adam Sutton + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "tvheadend.h" +#include "queue.h" +#include "epg.h" +#include "dvb/dvb.h" +#include "epggrab.h" +#include "epggrab/ota.h" + +LIST_HEAD(,epggrab_ota_mux) ota_muxes; + +epggrab_ota_mux_t *epggrab_ota_register + ( epggrab_module_t *mod, th_dvb_mux_instance_t *tdmi, + int timeout, int interval ) +{ + epggrab_ota_mux_t *ota; + + /* Check for existing */ + LIST_FOREACH(ota, &ota_muxes, glob_link) { + if (ota->grab == mod && ota->tdmi == tdmi) return ota; + } + + /* Install new */ + ota = calloc(1, sizeof(epggrab_ota_mux_t)); + ota->grab = mod; + ota->tdmi = tdmi; + ota->timeout = timeout; + ota->interval = interval; + LIST_INSERT_HEAD(&ota_muxes, ota, glob_link); + LIST_INSERT_HEAD(&tdmi->tdmi_epg_grabbers, ota, tdmi_link); + LIST_INSERT_HEAD(&mod->muxes, ota, grab_link); + return ota; +} + +/* + * Unregister + */ +void epggrab_ota_unregister ( epggrab_ota_mux_t *ota ) +{ + LIST_REMOVE(ota, glob_link); + LIST_REMOVE(ota, tdmi_link); + LIST_REMOVE(ota, grab_link); + free(ota); +} + +void epggrab_ota_unregister_one + ( epggrab_module_t *mod, th_dvb_mux_instance_t *tdmi ) +{ + epggrab_ota_mux_t *ota; + LIST_FOREACH(ota, &ota_muxes, glob_link) { + if (ota->grab == mod && (!tdmi || ota->tdmi == tdmi)) { + epggrab_ota_unregister(ota); + if (tdmi) break; + } + } +} + +void epggrab_ota_unregister_all + ( epggrab_module_t *mod ) +{ + epggrab_ota_unregister_one(mod, NULL); +} + +/* + * Status + */ +int epggrab_ota_begin ( epggrab_ota_mux_t *ota ) +{ + if (ota->status != EPGGRAB_OTA_STARTED) { + ota->status = EPGGRAB_OTA_STARTED; + time(&ota->started); + return 1; + } + return 0; +} + +void epggrab_ota_complete ( epggrab_ota_mux_t *ota ) +{ + if (ota->status != EPGGRAB_OTA_COMPLETED) + time(&ota->completed); + ota->status = EPGGRAB_OTA_COMPLETED; +} + +void epggrab_ota_cancel ( epggrab_ota_mux_t *ota ) +{ + ota->status = EPGGRAB_OTA_IDLE; +} + +void epggrab_ota_timeout ( epggrab_ota_mux_t *ota ) +{ + epggrab_ota_complete(ota); + // TODO: for now just treat as completed, stops it being allowed + // to immediately re-enter the queue +} diff --git a/src/epggrab/ota.h b/src/epggrab/ota.h new file mode 100644 index 00000000..0b8d4bff --- /dev/null +++ b/src/epggrab/ota.h @@ -0,0 +1,74 @@ +/* + * Electronic Program Guide - EPG grabber OTA functions + * Copyright (C) 2012 Adam Sutton + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __TVH_EPGGRAB_OTA_H__ +#define __TVH_EPGGRAB_OTA_H__ + +#include "queue.h" +#include + +/* + * Mux to OTA Grabber link + */ +typedef struct epggrab_ota_mux +{ + LIST_ENTRY(epggrab_ota_mux) glob_link; ///< Global list of all instances + LIST_ENTRY(epggrab_ota_mux) grab_link; ///< Grabber's list link + LIST_ENTRY(epggrab_ota_mux) tdmi_link; ///< Mux list link + struct epggrab_module *grab; ///< Grabber module + struct th_dvb_mux_instance *tdmi; ///< Mux instance + + int timeout; ///< Time out if this long + int interval; ///< Re-grab this often + + enum { + EPGGRAB_OTA_IDLE, + EPGGRAB_OTA_STARTED, + EPGGRAB_OTA_COMPLETED + } status; ///< Status of the scan + time_t started; ///< Time of last start + time_t completed; ///< Time of last completion + +} epggrab_ota_mux_t; + +/* + * Register interest (will return existing if already registered) + */ +epggrab_ota_mux_t *epggrab_ota_register + ( struct epggrab_module *mod, struct th_dvb_mux_instance *tdmi, + int timeout, int interval ); + +/* + * Unregister + */ +void epggrab_ota_unregister + ( epggrab_ota_mux_t *ota ); +void epggrab_ota_unregister_one + ( struct epggrab_module *mod, struct th_dvb_mux_instance *tdmi ); +void epggrab_ota_unregister_all + ( struct epggrab_module *mod ); + +/* + * Status + */ +int epggrab_ota_begin ( epggrab_ota_mux_t *ota ); +void epggrab_ota_complete ( epggrab_ota_mux_t *ota ); +void epggrab_ota_cancel ( epggrab_ota_mux_t *ota ); +void epggrab_ota_timeout ( epggrab_ota_mux_t *ota ); + +#endif /* __TVH_EPGGRAB_OTA_H__ */