diff --git a/Makefile b/Makefile index 537b25e7..43d95a67 100644 --- a/Makefile +++ b/Makefile @@ -74,11 +74,13 @@ SRCS = src/main.c \ src/avc.c \ src/huffman.c \ -SRCS += src/epggrab/pyepg.c\ - src/epggrab/xmltv.c\ - src/epggrab/ota.c \ - src/epggrab/eit.c \ - src/epggrab/opentv.c \ +SRCS += src/epggrab/module.c\ + src/epggrab/channel.c\ + src/epggrab/otamux.c\ + src/epggrab/module/pyepg.c\ + src/epggrab/module/xmltv.c\ + src/epggrab/module/eit.c \ + src/epggrab/module/opentv.c \ SRCS += src/plumbing/tsfix.c \ src/plumbing/globalheaders.c \ diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 1f843cfc..6f2baf0c 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -115,8 +115,6 @@ 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; @@ -132,10 +130,10 @@ typedef struct th_dvb_mux_instance { struct service_list tdmi_transports; /* via s_mux_link */ - TAILQ_ENTRY(th_dvb_mux_instance) tdmi_scan_link; struct th_dvb_mux_instance_queue *tdmi_scan_queue; + TAILQ_HEAD(, epggrab_ota_mux) tdmi_epg_grab; } th_dvb_mux_instance_t; @@ -147,8 +145,7 @@ typedef struct th_dvb_mux_instance { #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 +#define TDA_SCANQ_NUM 2 typedef struct th_dvb_adapter { @@ -164,6 +161,8 @@ typedef struct th_dvb_adapter { th_dvb_mux_instance_t *tda_mux_current; + th_dvb_mux_instance_t *tda_mux_epg; + int tda_table_epollfd; const char *tda_rootpath; diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 12e13066..77427bbc 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -41,7 +41,7 @@ #include "tsdemux.h" #include "notify.h" #include "service.h" -#include "epggrab/ota.h" +#include "epggrab.h" struct th_dvb_adapter_queue dvb_adapters; struct th_dvb_mux_instance_tree dvb_muxes; @@ -420,16 +420,10 @@ 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 @@ -449,15 +443,16 @@ dvb_adapter_mux_scanner(void *aux) return; } - /* 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); - } + /* Check EPG */ + if (tda->tda_mux_epg) { + epggrab_mux_stop(tda->tda_mux_epg, 1); // timeout anything not complete + tda->tda_mux_epg = NULL; // skip this time + } else { + tda->tda_mux_epg = epggrab_mux_next(tda); + } /* Idle or EPG scan enabled */ - idle_epg = tda->tda_idlescan || - TAILQ_FIRST(&tda->tda_scan_queues[TDA_SCANQ_EPG]); + idle_epg = tda->tda_idlescan || tda->tda_mux_epg; /* Idlescan is disabled and no muxes are bad */ if(!idle_epg && TAILQ_FIRST(&tda->tda_scan_queues[TDA_SCANQ_BAD]) == NULL) { @@ -472,27 +467,27 @@ dvb_adapter_mux_scanner(void *aux) return; } - /* 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; + /* EPG */ + if (tda->tda_mux_epg) { + int period = epggrab_mux_period(tda->tda_mux_epg); + if (period > 20) + gtimer_arm(&tda->tda_mux_scanner_timer, + dvb_adapter_mux_scanner, tda, period); + dvb_fe_tune(tda->tda_mux_epg, "EPG scan"); - tdmi = TAILQ_FIRST(&tda->tda_scan_queues[tda->tda_scan_selector]); - if(tdmi != NULL) { + /* Normal */ + } else { - /* 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); + /* Alternate queue */ + 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) { + dvb_fe_tune(tdmi, "Autoscan"); + return; } - - 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 79087940..388fe5ef 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -230,6 +230,8 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi) if(tdmi->tdmi_enabled) { dvb_mux_add_to_scan_queue(tdmi); } + + epggrab_mux_stop(tdmi, 0); time(&tdmi->tdmi_lost_adapter); } @@ -509,7 +511,7 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) dvb_table_add_default(tdmi); - epggrab_tune(tdmi); + epggrab_mux_start(tdmi); dvb_adapter_notify(tda); return 0; diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 8a4b8882..404e5877 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -44,7 +44,7 @@ #include "dvb_support.h" #include "notify.h" #include "subscriptions.h" -#include "epggrab/ota.h" +#include "epggrab.h" struct th_dvb_mux_instance_tree dvb_muxes; @@ -289,6 +289,8 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, mux_link_initial(tda, tdmi); } + TAILQ_INIT(&tdmi->tdmi_epg_grab); + return tdmi; } @@ -298,7 +300,6 @@ 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; @@ -332,8 +333,7 @@ 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); + epggrab_mux_delete(tdmi); hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier); @@ -1157,9 +1157,8 @@ void dvb_mux_add_to_scan_queue ( th_dvb_mux_instance_t *tdmi ) { int ti; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - ti = LIST_FIRST(&tdmi->tdmi_epg_grabbers) ? TDA_SCANQ_EPG - : tdmi->tdmi_quality == 100 ? TDA_SCANQ_OK - : TDA_SCANQ_BAD; + ti = 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/epggrab.c b/src/epggrab.c index 08bb5f3b..8036d733 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -13,10 +13,7 @@ #include "queue.h" #include "epg.h" #include "epggrab.h" -#include "epggrab/eit.h" -#include "epggrab/xmltv.h" -#include "epggrab/pyepg.h" -#include "epggrab/opentv.h" +#include "epggrab/private.h" #include "channels.h" #include "spawn.h" #include "htsmsg_xml.h" @@ -30,59 +27,20 @@ pthread_cond_t epggrab_cond; /* Config */ uint32_t epggrab_interval; -epggrab_module_t* epggrab_module; +epggrab_module_int_t* epggrab_module; epggrab_module_list_t epggrab_modules; uint32_t epggrab_channel_rename; uint32_t epggrab_channel_renumber; uint32_t epggrab_channel_reicon; /* ************************************************************************** - * Helpers + * Internal Grab Thread * *************************************************************************/ -/* - * Run the parse - */ -static void _epggrab_module_parse - ( epggrab_module_t *mod, htsmsg_t *data ) -{ - time_t tm1, tm2; - int save = 0; - epggrab_stats_t stats; - - /* Parse */ - memset(&stats, 0, sizeof(stats)); - pthread_mutex_lock(&global_lock); - time(&tm1); - save |= mod->parse(mod, data, &stats); - time(&tm2); - if (save) epg_updated(); - pthread_mutex_unlock(&global_lock); - htsmsg_destroy(data); - - /* Debug stats */ - tvhlog(LOG_INFO, mod->id, "parse took %d seconds", tm2 - tm1); - tvhlog(LOG_INFO, mod->id, " channels tot=%5d new=%5d mod=%5d", - stats.channels.total, stats.channels.created, - stats.channels.modified); - tvhlog(LOG_INFO, mod->id, " brands tot=%5d new=%5d mod=%5d", - stats.brands.total, stats.brands.created, - stats.brands.modified); - tvhlog(LOG_INFO, mod->id, " seasons tot=%5d new=%5d mod=%5d", - stats.seasons.total, stats.seasons.created, - stats.seasons.modified); - tvhlog(LOG_INFO, mod->id, " episodes tot=%5d new=%5d mod=%5d", - stats.episodes.total, stats.episodes.created, - stats.episodes.modified); - tvhlog(LOG_INFO, mod->id, " broadcasts tot=%5d new=%5d mod=%5d", - stats.broadcasts.total, stats.broadcasts.created, - stats.broadcasts.modified); -} - /* * Grab from module */ -static void _epggrab_module_grab ( epggrab_module_t *mod ) +static void _epggrab_module_grab ( epggrab_module_int_t *mod ) { time_t tm1, tm2; htsmsg_t *data; @@ -95,43 +53,12 @@ static void _epggrab_module_grab ( epggrab_module_t *mod ) /* Process */ if ( data ) { tvhlog(LOG_INFO, mod->id, "grab took %d seconds", tm2 - tm1); - _epggrab_module_parse(mod, data); + epggrab_module_parse(mod, data); } else { tvhlog(LOG_WARNING, mod->id, "grab returned no data"); } } -/* - * Socket handler - */ -static void _epggrab_socket_handler ( epggrab_module_t *mod, int s ) -{ - size_t outlen; - char *outbuf; - time_t tm1, tm2; - htsmsg_t *data = NULL; - - /* Grab/Translate */ - time(&tm1); - outlen = file_readall(s, &outbuf); - if (outlen) data = mod->trans(mod, outbuf); - time(&tm2); - - /* Process */ - if ( data ) { - tvhlog(LOG_INFO, mod->id, "grab took %d seconds", tm2 - tm1); - _epggrab_module_parse(mod, data); - - /* Failed */ - } else { - tvhlog(LOG_ERR, mod->id, "failed to read data"); - } -} - -/* ************************************************************************** - * Threads - * *************************************************************************/ - /* * Thread (for internal grabbing) */ @@ -139,7 +66,7 @@ static void* _epggrab_internal_thread ( void* p ) { int err, confver = -1; // force first run struct timespec ts; - epggrab_module_t *mod; + epggrab_module_int_t *mod; /* Setup timeout */ ts.tv_nsec = 0; @@ -169,476 +96,6 @@ static void* _epggrab_internal_thread ( void* p ) return NULL; } -/* - * External (socket) grab thread - */ -static void *_epggrab_socket_thread ( void *p ) -{ - int s; - epggrab_module_t *mod = (epggrab_module_t*)p; - tvhlog(LOG_INFO, mod->id, "external socket enabled"); - - while ( mod->enabled && mod->sock ) { - tvhlog(LOG_DEBUG, mod->id, "waiting for connection"); - s = accept(mod->sock, NULL, NULL); - if (s <= 0) continue; - tvhlog(LOG_DEBUG, mod->id, "got connection %d", s); - _epggrab_socket_handler(mod, s); - } - tvhlog(LOG_DEBUG, mod->id, "terminated"); - return NULL; -} - -/* ************************************************************************** - * Base Module functions - * *************************************************************************/ - -static int _ch_id_cmp ( void *a, void *b ) -{ - return strcmp(((epggrab_channel_t*)a)->id, - ((epggrab_channel_t*)b)->id); -} - -static int _ch_link ( epggrab_channel_t *ec, channel_t *ch ) -{ - service_t *sv; - int match = 0, i; - - if (!ec || !ch) return 0; - if (ec->channel) return 0; - - if (ec->name && !strcmp(ec->name, ch->ch_name)) - match = 1; - else { - LIST_FOREACH(sv, &ch->ch_services, s_ch_link) { - if (ec->sid) { - for (i = 0; i < ec->sid_cnt; i++ ) { - if (sv->s_dvb_service_id == ec->sid[i]) { - match = 1; - break; - } - } - } - if (!match && ec->sname) { - i = 0; - while (ec->sname[i]) { - if (!strcmp(ec->sname[i], sv->s_svcname)) { - match = 1; - break; - } - i++; - } - } - if (match) break; - } - } - - if (match) { - tvhlog(LOG_INFO, ec->mod->id, "linking %s to %s", - ec->id, ch->ch_name); - ec->channel = ch; - if (ec->name && epggrab_channel_rename) - channel_rename(ch, ec->name); - if (ec->icon && epggrab_channel_reicon) - channel_set_icon(ch, ec->icon); - if (ec->number>0 && epggrab_channel_renumber) - channel_set_number(ch, ec->number); - } - - return match; -} - -int epggrab_module_enable_socket ( epggrab_module_t *mod, uint8_t e ) -{ - pthread_t tid; - pthread_attr_t tattr; - struct sockaddr_un addr; - assert(mod->path); - assert(mod->flags & EPGGRAB_MODULE_EXTERNAL); - - /* Ignore */ - if ( mod->enabled == e ) return 0; - - /* Disable */ - if (!e) { - shutdown(mod->sock, SHUT_RDWR); - close(mod->sock); - unlink(mod->path); - mod->sock = 0; - - /* Enable */ - } else { - unlink(mod->path); // just in case! - - mod->sock = socket(AF_UNIX, SOCK_STREAM, 0); - assert(mod->sock); - - memset(&addr, 0, sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, mod->path, 100); - if ( bind(mod->sock, (struct sockaddr*)&addr, - sizeof(struct sockaddr_un)) != 0 ) { - tvhlog(LOG_ERR, mod->id, "failed to bind socket"); - close(mod->sock); - mod->sock = 0; - return 0; - } - - if ( listen(mod->sock, 5) != 0 ) { - tvhlog(LOG_ERR, mod->id, "failed to listen on socket"); - close(mod->sock); - mod->sock = 0; - return 0; - } - - tvhlog(LOG_DEBUG, mod->id, "starting socket thread"); - pthread_attr_init(&tattr); - pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); - pthread_create(&tid, &tattr, _epggrab_socket_thread, mod); - } - mod->enabled = e; - return 1; -} - -const char *epggrab_module_socket_path ( const char *id ) -{ - char *ret = malloc(100); - snprintf(ret, 100, "%s/epggrab/%s.sock", - hts_settings_get_root(), id); - return ret; -} - -char *epggrab_module_grab ( epggrab_module_t *mod ) -{ - int outlen; - char *outbuf; - - /* Debug */ - tvhlog(LOG_INFO, mod->id, "grab %s", mod->path); - - /* Grab */ - outlen = spawn_and_store_stdout(mod->path, NULL, &outbuf); - if ( outlen < 1 ) { - tvhlog(LOG_ERR, "pyepg", "no output detected"); - return NULL; - } - - return outbuf; -} - -htsmsg_t *epggrab_module_trans_xml - ( epggrab_module_t *mod, char *c ) -{ - htsmsg_t *ret; - char errbuf[100]; - - if (!c) return NULL; - - /* Extract */ - ret = htsmsg_xml_deserialize(c, errbuf, sizeof(errbuf)); - if (!ret) - tvhlog(LOG_ERR, "pyepg", "htsmsg_xml_deserialize error %s", errbuf); - return ret; -} - -void epggrab_module_channel_save - ( epggrab_module_t *mod, epggrab_channel_t *ch ) -{ - int i; - htsmsg_t *m = htsmsg_create_map(); - htsmsg_t *a; - - if (ch->name) - htsmsg_add_str(m, "name", ch->name); - if (ch->icon) - htsmsg_add_str(m, "icon", ch->icon); - if (ch->channel) - htsmsg_add_u32(m, "channel", ch->channel->ch_id); - if (ch->sid) { - a = htsmsg_create_list(); - for (i = 0; i < ch->sid_cnt; i++ ) { - htsmsg_add_u32(a, NULL, ch->sid[i]); - } - htsmsg_add_msg(m, "sid", a); - } - if (ch->sname) { - a = htsmsg_create_list(); - i = 0; - while (ch->sname[i]) { - htsmsg_add_str(a, NULL, ch->sname[i]); - i++; - } - htsmsg_add_msg(m, "sname", a); - } - if (ch->number) - htsmsg_add_u32(m, "number", ch->number); - - hts_settings_save(m, "epggrab/%s/channels/%s", mod->id, ch->id); -} - -static void epggrab_module_channel_load - ( epggrab_module_t *mod, htsmsg_t *m, const char *id ) -{ - int save = 0, i; - const char *str; - uint32_t u32; - htsmsg_t *a; - htsmsg_field_t *f; - - epggrab_channel_t *ch = epggrab_module_channel_find(mod, id, 1, &save); - - if ((str = htsmsg_get_str(m, "name"))) - ch->name = strdup(str); - if ((str = htsmsg_get_str(m, "icon"))) - ch->icon = strdup(str); - if ((a = htsmsg_get_list(m, "sid"))) { - i = 0; - HTSMSG_FOREACH(f, a) i++; - ch->sid_cnt = i; - ch->sid = calloc(i, sizeof(uint16_t)); - i = 0; - HTSMSG_FOREACH(f, a) { - ch->sid[i] = (uint16_t)f->hmf_s64; - i++; - } - } - if ((a = htsmsg_get_list(m, "sname"))) { - i = 0; - HTSMSG_FOREACH(f, a) i++; - ch->sname = calloc(i+1, sizeof(char*)); - i = 0; - HTSMSG_FOREACH(f, a) { - ch->sname[i] = strdup(f->hmf_str); - i++; - } - } - if(!htsmsg_get_u32(m, "number", &u32)) - ch->number = u32; - - if (!htsmsg_get_u32(m, "channel", &u32)) - ch->channel = channel_find_by_identifier(u32); -} - -void epggrab_module_channels_load ( epggrab_module_t *mod ) -{ - htsmsg_t *m, *e; - htsmsg_field_t *f; - - if ((m = hts_settings_load("epggrab/%s/channels", mod->id))) { - HTSMSG_FOREACH(f, m) { - if ((e = htsmsg_get_map_by_field(f))) - epggrab_module_channel_load(mod, e, f->hmf_name); - } - htsmsg_destroy(m); - } -} - -void epggrab_module_channel_add ( epggrab_module_t *mod, channel_t *ch ) -{ - epggrab_channel_t *egc; - RB_FOREACH(egc, mod->channels, link) { - if (_ch_link(egc, ch) ) { - epggrab_module_channel_save(mod, egc); - break; - } - } -} - -void epggrab_module_channel_rem ( epggrab_module_t *mod, channel_t *ch ) -{ - epggrab_channel_t *egc; - RB_FOREACH(egc, mod->channels, link) { - if (egc->channel == ch) { - egc->channel = NULL; - epggrab_module_channel_save(mod, egc); - break; - } - } -} - -void epggrab_module_channel_mod ( epggrab_module_t *mod, channel_t *ch ) -{ - return epggrab_module_channel_add(mod, ch); -} - - -epggrab_channel_t *epggrab_module_channel_find - ( epggrab_module_t *mod, const char *id, int create, int *save ) -{ - epggrab_channel_t *ec; - static epggrab_channel_t *skel = NULL; - - if (!mod || !mod->channels ) return NULL; - - if ( !skel ) skel = calloc(1, sizeof(epggrab_channel_t)); - skel->id = (char*)id; - skel->mod = mod; - - /* Find */ - if (!create) { - ec = RB_FIND(mod->channels, skel, link, _ch_id_cmp); - - /* Create (if required) */ - } else { - ec = RB_INSERT_SORTED(mod->channels, skel, link, _ch_id_cmp); - if ( ec == NULL ) { - skel->id = strdup(skel->id); - ec = skel; - skel = NULL; - *save = 1; - } - } - - return ec; -} - -/* ************************************************************************** - * Channels - * *************************************************************************/ - -int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name ) -{ - int save = 0; - if (!ec || !name) return 0; - if (!ec->name || strcmp(ec->name, name)) { - if (ec->name) free(ec->name); - ec->name = strdup(name); - if (ec->channel && epggrab_channel_rename) - channel_rename(ec->channel, name); - save = 1; - } - return save; -} - -int epggrab_channel_set_sid - ( epggrab_channel_t *ec, const uint16_t *sid, int num ) -{ - int save = 0, i = 0; - if ( !ec || !sid ) return 0; - if (!ec->sid) save = 1; - else if (ec->sid_cnt != num) save = 1; - else { - for (i = 0; i < num; i++ ) { - if (sid[i] != ec->sid[i]) { - save = 1; - break; - } - } - } - if (save) { - if (ec->sid) free(ec->sid); - ec->sid = calloc(num, sizeof(uint16_t)); - for (i = 0; i < num; i++ ) { - ec->sid[i] = sid[i]; - } - ec->sid_cnt = num; - } - return save; -} - -int epggrab_channel_set_sname ( epggrab_channel_t *ec, const char **sname ) -{ - int save = 0, i = 0; - if ( !ec || !sname ) return 0; - if (!ec->sname) save = 1; - else { - while ( ec->sname[i] && sname[i] ) { - if (strcmp(ec->sname[i], sname[i])) { - save = 1; - break; - } - i++; - } - if (!save && (ec->sname[i] || sname[i])) save = 1; - } - if (save) { - if (ec->sname) { - i = 0; - while (ec->sname[i]) - free(ec->sname[i++]); - free(ec->sname); - } - i = 0; - while (sname[i++]); - ec->sname = calloc(i+1, sizeof(char*)); - i = 0; - while (sname[i]) { - ec->sname[i] = strdup(sname[i]); - i++; - } - } - return save; -} - -int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon ) -{ - int save = 0; - if (!ec->icon || strcmp(ec->icon, icon) ) { - if (!ec | !icon) return 0; - if (ec->icon) free(ec->icon); - ec->icon = strdup(icon); - if (ec->channel && epggrab_channel_reicon) - channel_set_icon(ec->channel, icon); - save = 1; - } - return save; -} - -int epggrab_channel_set_number ( epggrab_channel_t *ec, int number ) -{ - int save = 0; - if (!ec || (number <= 0)) return 0; - if (ec->number != number) { - ec->number = number; - if (ec->channel && epggrab_channel_renumber) - channel_set_number(ec->channel, number); - save = 1; - } - return save; -} - -void epggrab_channel_link ( epggrab_channel_t *ec ) -{ - channel_t *ch; - - if (!ec) return; - - /* Link */ - if (!ec->channel) { - RB_FOREACH(ch, &channel_name_tree, ch_name_link) { - if (_ch_link(ec, ch)) break; - } - } -} - -void epggrab_channel_updated ( epggrab_channel_t *ec ) -{ - epggrab_channel_link(ec); - epggrab_module_channel_save(ec->mod, ec); -} - -htsmsg_t *epggrab_channel_list ( void ) -{ - char name[100]; - epggrab_module_t *mod; - epggrab_channel_t *ec; - htsmsg_t *e, *m; - m = htsmsg_create_list(); - LIST_FOREACH(mod, &epggrab_modules, link) { - if (mod->enabled && mod->channels) { - RB_FOREACH(ec, mod->channels, link) { - e = htsmsg_create_map(); - htsmsg_add_str(e, "module", mod->id); - htsmsg_add_str(e, "id", ec->id); - sprintf(name, "%s: %s", mod->name, ec->name); - htsmsg_add_str(e, "name", name); - htsmsg_add_msg(m, NULL, e); - } - } - } - return m; -} - /* ************************************************************************** * Configuration * *************************************************************************/ @@ -667,8 +124,12 @@ static void _epggrab_load ( void ) if (old) epggrab_interval *= 3600; htsmsg_get_u32(m, "grab-enabled", &enabled); if (enabled) { - if ( (str = htsmsg_get_str(m, old ? "current-grabber" : "module")) ) - epggrab_module = epggrab_module_find_by_id(str); + if ( (str = htsmsg_get_str(m, old ? "current-grabber" : "module")) ) { + mod = epggrab_module_find_by_id(str); + if (mod && mod->type == EPGGRAB_INT) { + epggrab_module = (epggrab_module_int_t*)mod; + } + } if ( (a = htsmsg_get_map(m, "mod_enabled")) ) { LIST_FOREACH(mod, &epggrab_modules, link) { if (htsmsg_get_u32_or_default(a, mod->id, 0)) { @@ -712,7 +173,7 @@ static void _epggrab_load ( void ) epggrab_interval = 12 * 3600; // hours epggrab_module = NULL; // disabled LIST_FOREACH(m, &epggrab_modules, link) // enable all OTA by default - if (m->flags & EPGGRAB_MODULE_OTA) + if (m->type == EPGGRAB_OTA) epggrab_enable_module(m, 1); } @@ -762,18 +223,18 @@ int epggrab_set_interval ( uint32_t interval ) return save; } -int epggrab_set_module ( epggrab_module_t *mod ) +int epggrab_set_module ( epggrab_module_t *m ) { int save = 0; + epggrab_module_int_t *mod; + if (m && m->type != EPGGRAB_INT) return 0; + mod = (epggrab_module_int_t*)m; if ( epggrab_module != mod ) { - if (epggrab_module) epggrab_enable_module(epggrab_module, 0); - if (mod) { - assert(mod->grab); - assert(mod->trans); - assert(mod->parse); - } - epggrab_module = mod; - if (epggrab_module) epggrab_enable_module(epggrab_module, 1); + if (epggrab_module && epggrab_module->enable) + epggrab_module->enable(epggrab_module, 0); + epggrab_module = (epggrab_module_int_t*)mod; + if (epggrab_module && epggrab_module->enable) + epggrab_module->enable(epggrab_module, 1); save = 1; } return save; @@ -832,19 +293,30 @@ int epggrab_enable_module_by_id ( const char *id, uint8_t e ) } /* ************************************************************************** - * Global Functions + * Initialisation * *************************************************************************/ +/* + * TODO: implement this + */ +void epggrab_resched ( void ) +{ +} + /* * Initialise */ void epggrab_init ( void ) { + /* Lists */ + extern TAILQ_HEAD(, epggrab_ota_mux) ota_mux_all; + TAILQ_INIT(&ota_mux_all); + /* Initialise modules */ - eit_init(&epggrab_modules); - xmltv_init(&epggrab_modules); - pyepg_init(&epggrab_modules); - opentv_init(&epggrab_modules); + eit_init(); + xmltv_init(); + pyepg_init(); + opentv_init(); /* Load config */ _epggrab_load(); @@ -857,71 +329,3 @@ void epggrab_init ( void ) pthread_create(&tid, &tattr, _epggrab_internal_thread, NULL); } -void epggrab_channel_add ( channel_t *ch ) -{ - epggrab_module_t *m; - LIST_FOREACH(m, &epggrab_modules, link) { - if (m->ch_add) m->ch_add(m, ch); - } -} - -void epggrab_channel_rem ( channel_t *ch ) -{ - epggrab_module_t *m; - LIST_FOREACH(m, &epggrab_modules, link) { - if (m->ch_rem) m->ch_rem(m, ch); - } -} - -void epggrab_channel_mod ( channel_t *ch ) -{ - epggrab_module_t *m; - LIST_FOREACH(m, &epggrab_modules, link) { - if (m->ch_mod) m->ch_mod(m, 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); - } -} - -epggrab_module_t* epggrab_module_find_by_id ( const char *id ) -{ - epggrab_module_t *m; - LIST_FOREACH(m, &epggrab_modules, link) { - if ( !strcmp(m->id, id) ) return m; - } - return NULL; -} - -htsmsg_t *epggrab_module_list ( void ) -{ - epggrab_module_t *m; - htsmsg_t *e, *a = htsmsg_create_list(); - LIST_FOREACH(m, &epggrab_modules, link) { - e = htsmsg_create_map(); - htsmsg_add_str(e, "id", m->id); - if(m->name) htsmsg_add_str(e, "name", m->name); - if(m->path) htsmsg_add_str(e, "path", m->path); - htsmsg_add_u32(e, "flags", m->flags); - htsmsg_add_u32(e, "enabled", m->enabled); - htsmsg_add_msg(a, NULL, e); - } - return a; -} diff --git a/src/epggrab.h b/src/epggrab.h index 7a6cdbb6..f4d8b019 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -1,12 +1,41 @@ +/* + * EPG Grabber - common 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 __EPGGRAB_H__ #define __EPGGRAB_H__ -typedef struct epggrab_module epggrab_module_t; - #include -#include "channels.h" -#include "dvb/dvb.h" -#include "epggrab/ota.h" + +/* ************************************************************************** + * Typedefs/Forward decls + * *************************************************************************/ + +struct th_dvb_mux_instance; +struct th_dvb_adapter; + +typedef struct epggrab_module epggrab_module_t; +typedef struct epggrab_module_int epggrab_module_int_t; +typedef struct epggrab_module_ext epggrab_module_ext_t; +typedef struct epggrab_module_ota epggrab_module_ota_t; +typedef struct epggrab_ota_mux epggrab_ota_mux_t; + +LIST_HEAD(epggrab_module_list, epggrab_module); +typedef struct epggrab_module_list epggrab_module_list_t; /* ************************************************************************** * Grabber Stats @@ -41,14 +70,14 @@ typedef struct epggrab_channel epggrab_module_t *mod; ///< Linked module char *id; ///< Grabber's ID + char *name; ///< Channel name - char **sname; ///< Service name's - uint16_t *sid; ///< Service ID's - int sid_cnt; ///< Number of service IDs - char *icon; ///< Channel icon int number; ///< Channel number + char **sname; ///< Service name's + uint16_t *sid; ///< Service ID's + struct channel *channel; ///< Mapped channel } epggrab_channel_t; @@ -67,88 +96,113 @@ htsmsg_t* epggrab_channel_list ( void ); * Mutators */ int epggrab_channel_set_name ( epggrab_channel_t *ch, const char *name ); -int epggrab_channel_set_sid ( epggrab_channel_t *ch, const uint16_t *sid, int num ); -int epggrab_channel_set_sname ( epggrab_channel_t *ch, const char **sname ); int epggrab_channel_set_icon ( epggrab_channel_t *ch, const char *icon ); int epggrab_channel_set_number ( epggrab_channel_t *ch, int number ); - -void epggrab_channel_updated ( epggrab_channel_t *ch ); -void epggrab_channel_link ( epggrab_channel_t *ch ); +int epggrab_channel_set_sname ( epggrab_channel_t *ch, const char **sname ); +int epggrab_channel_set_sid ( epggrab_channel_t *ch, const uint16_t *sid ); /* - * Match the channel + * Updated/link */ +void epggrab_channel_updated ( epggrab_channel_t *ch ); /* ************************************************************************** * Grabber Modules * *************************************************************************/ -/* - * Grabber flags - */ -#define EPGGRAB_MODULE_INTERNAL 0x01 ///< IP based internally run -#define EPGGRAB_MODULE_EXTERNAL 0x02 ///< IP based externally run -#define EPGGRAB_MODULE_OTA 0x04 ///< DVB OTA EPGs - /* * Grabber base class */ struct epggrab_module { - LIST_ENTRY(epggrab_module) link; ///< Global list link + LIST_ENTRY(epggrab_module) link; ///< Global list link + enum { + EPGGRAB_INT, + EPGGRAB_EXT, + EPGGRAB_OTA + } type; ///< Grabber type 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 ); - - /* Grab/Translate/Parse */ - char* (*grab) ( epggrab_module_t *m ); - htsmsg_t* (*trans) ( epggrab_module_t *m, char *d ); - int (*parse) ( epggrab_module_t *m, - htsmsg_t *d, epggrab_stats_t *s ); + int (*enable) ( void *m, uint8_t e ); /* Channel listings */ - void (*ch_add) ( epggrab_module_t *m, channel_t *ch ); - void (*ch_rem) ( epggrab_module_t *m, channel_t *ch ); - void (*ch_mod) ( epggrab_module_t *m, channel_t *ch ); - - /* Transponder tuning */ - void (*tune) ( epggrab_module_t *m, th_dvb_mux_instance_t *tdmi ); + void (*ch_add) ( void *m, struct channel *ch ); + void (*ch_rem) ( void *m, struct channel *ch ); + void (*ch_mod) ( void *m, struct channel *ch ); + void (*ch_save) ( void *m, epggrab_channel_t *ch ); }; /* - * Default module functions + * Internal grabber */ +struct epggrab_module_int +{ + epggrab_module_t ; ///< Parent object -int epggrab_module_enable_socket ( epggrab_module_t *m, uint8_t e ); -const char *epggrab_module_socket_path ( const char *id ); + const char *path; ///< Path for the command -char *epggrab_module_grab ( epggrab_module_t *m ); -htsmsg_t *epggrab_module_trans_xml ( epggrab_module_t *m, char *d ); - -void epggrab_module_channel_save - ( epggrab_module_t *m, epggrab_channel_t *ch ); -void epggrab_module_channels_load ( epggrab_module_t *m ); -void epggrab_module_channel_add ( epggrab_module_t *m, channel_t *ch ); -void epggrab_module_channel_rem ( epggrab_module_t *m, channel_t *ch ); -void epggrab_module_channel_mod ( epggrab_module_t *m, channel_t *ch ); - -epggrab_channel_t *epggrab_module_channel_find - ( epggrab_module_t *mod, const char *id, int create, int *save ); + /* Handle data */ + char* (*grab) ( void *mod ); + htsmsg_t* (*trans) ( void *mod, char *data ); + int (*parse) ( void *mod, htsmsg_t *data, epggrab_stats_t *stat ); +}; /* - * Module list + * External grabber */ -LIST_HEAD(epggrab_module_list, epggrab_module); -typedef struct epggrab_module_list epggrab_module_list_t; +struct epggrab_module_ext +{ + epggrab_module_int_t ; ///< Parent object + + int sock; ///< Socket descriptor +}; + +/* + * OTA / mux link + */ +struct epggrab_ota_mux +{ + TAILQ_ENTRY(epggrab_ota_mux) glob_link; ///< Grabber link + TAILQ_ENTRY(epggrab_ota_mux) tdmi_link; ///< Link to mux + TAILQ_ENTRY(epggrab_ota_mux) grab_link; ///< Link to grabber + struct th_dvb_mux_instance *tdmi; ///< Mux instance + epggrab_module_ota_t *grab; ///< Grab instance + + int timeout; ///< Time out if this long + int interval; ///< Re-grab this often + + int is_reg; ///< Permanently registered + + void *status; ///< Status information + enum { + EPGGRAB_OTA_MUX_IDLE, + EPGGRAB_OTA_MUX_RUNNING, + EPGGRAB_OTA_MUX_TIMEDOUT, + EPGGRAB_OTA_MUX_COMPLETE + } state; ///< Current state + time_t started; ///< Time of last start + time_t completed; ///< Time of last completion + + void (*destroy) (epggrab_ota_mux_t *ota); ///< (Custom) destroy +}; + +/* + * Over the air grabber + */ +struct epggrab_module_ota +{ + epggrab_module_t ; ///< Parent object + + TAILQ_HEAD(, epggrab_ota_mux) muxes; ///< List of related muxes + + /* Transponder tuning */ + void (*start) ( epggrab_module_ota_t *m, struct th_dvb_mux_instance *tdmi ); +}; /* * Access the Module list @@ -157,38 +211,42 @@ epggrab_module_t* epggrab_module_find_by_id ( const char *id ); htsmsg_t* epggrab_module_list ( void ); /* ************************************************************************** - * Configuration + * Setup/Configuration * *************************************************************************/ /* * Configuration */ -extern pthread_mutex_t epggrab_mutex; -extern uint32_t epggrab_interval; -extern epggrab_module_t* epggrab_module; -extern uint32_t epggrab_channel_rename; -extern uint32_t epggrab_channel_renumber; -extern uint32_t epggrab_channel_reicon; +extern epggrab_module_list_t epggrab_modules; +extern pthread_mutex_t epggrab_mutex; +extern uint32_t epggrab_interval; +extern epggrab_module_int_t* epggrab_module; +extern uint32_t epggrab_channel_rename; +extern uint32_t epggrab_channel_renumber; +extern uint32_t epggrab_channel_reicon; /* - * Update + * Set configuration */ int epggrab_set_interval ( uint32_t interval ); -int epggrab_set_module ( epggrab_module_t *module ); +int epggrab_set_module ( epggrab_module_t *mod ); int epggrab_set_module_by_id ( const char *id ); int epggrab_set_channel_rename ( uint32_t e ); int epggrab_set_channel_renumber ( uint32_t e ); int epggrab_set_channel_reicon ( uint32_t e ); -int epggrab_enable_module ( epggrab_module_t *module, uint8_t e ); +int epggrab_enable_module ( epggrab_module_t *mod, uint8_t e ); int epggrab_enable_module_by_id ( const char *id, uint8_t e ); + +/* + * Load/Save + */ +void epggrab_init ( void ); void epggrab_save ( void ); /* ************************************************************************** * Global Functions * *************************************************************************/ -void epggrab_init ( void ); - /* * Channel handling */ @@ -199,6 +257,21 @@ void epggrab_channel_mod ( struct channel *ch ); /* * Transport handling */ -void epggrab_tune ( th_dvb_mux_instance_t *tdmi ); +void epggrab_mux_start ( struct th_dvb_mux_instance *tdmi ); +void epggrab_mux_stop ( struct th_dvb_mux_instance *tdmi, int timeout ); +void epggrab_mux_delete ( struct th_dvb_mux_instance *tdmi ); +int epggrab_mux_period ( struct th_dvb_mux_instance *tdmi ); +struct th_dvb_mux_instance *epggrab_mux_next ( struct th_dvb_adapter *tda ); + +/* + * Re-schedule + */ +void epggrab_resched ( void ); #endif /* __EPGGRAB_H__ */ + +/* ************************************************************************** + * Editor + * + * vim:sts=2:ts=2:sw=2:et + * *************************************************************************/ diff --git a/src/epggrab/channel.c b/src/epggrab/channel.c new file mode 100644 index 00000000..634f5d8c --- /dev/null +++ b/src/epggrab/channel.c @@ -0,0 +1,283 @@ +/* + * EPG Grabber - channel 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 +#include + +#include "tvheadend.h" +#include "settings.h" +#include "htsmsg.h" +#include "channels.h" +#include "service.h" +#include "epg.h" +#include "epggrab.h" +#include "epggrab/private.h" + +/* ************************************************************************** + * EPG Grab Channel functions + * *************************************************************************/ + +/* Link epggrab channel to real channel */ +// returns 1 if link made +int epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch ) +{ + service_t *sv; + int match = 0, i; + + if (!ec || !ch) return 0; + if (ec->channel) return 0; + + if (ec->name && !strcmp(ec->name, ch->ch_name)) + match = 1; + else { + LIST_FOREACH(sv, &ch->ch_services, s_ch_link) { + if (ec->sid) { + i = 0; + while (ec->sid[i]) { + if (sv->s_dvb_service_id == ec->sid[i]) { + match = 1; + break; + } + i++; + } + } + if (!match && ec->sname) { + i = 0; + while (ec->sname[i]) { + if (!strcmp(ec->sname[i], sv->s_svcname)) { + match = 1; + break; + } + i++; + } + } + if (match) break; + } + } + + if (match) { + tvhlog(LOG_INFO, ec->mod->id, "linking %s to %s", + ec->id, ch->ch_name); + ec->channel = ch; + if (ec->name && epggrab_channel_rename) + channel_rename(ch, ec->name); + if (ec->icon && epggrab_channel_reicon) + channel_set_icon(ch, ec->icon); + if (ec->number>0 && epggrab_channel_renumber) + channel_set_number(ch, ec->number); + } + + return match; +} + +/* Set name */ +int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name ) +{ + int save = 0; + if (!ec || !name) return 0; + if (!ec->name || strcmp(ec->name, name)) { + if (ec->name) free(ec->name); + ec->name = strdup(name); + if (ec->channel && epggrab_channel_rename) + channel_rename(ec->channel, name); + save = 1; + } + return save; +} + +/* Set icon */ +int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon ) +{ + int save = 0; + if (!ec->icon || strcmp(ec->icon, icon) ) { + if (!ec | !icon) return 0; + if (ec->icon) free(ec->icon); + ec->icon = strdup(icon); + if (ec->channel) channel_set_icon(ec->channel, icon); + save = 1; + } + return save; +} + +/* Set channel number */ +int epggrab_channel_set_number ( epggrab_channel_t *ec, int number ) +{ + int save = 0; + if (!ec || (number <= 0)) return 0; + if (ec->number != number) { + ec->number = number; + if (ec->channel) channel_set_number(ec->channel, number); + save = 1; + } + return save; +} + +/* Set service IDs */ +int epggrab_channel_set_sid + ( epggrab_channel_t *ec, const uint16_t *sid ) +{ + int save = 0, i; + if ( !ec || !sid ) return 0; + if (!ec->sid) save = 1; + else { + i = 0; + while ( ec->sid[i] && sid[i] ) { + if ( ec->sid[i] != sid[i] ) break; + i++; + } + if (ec->sid[i] || sid[i]) save = 1; + } + if (save) { + i = 0; + while (ec->sid[i++]); + if (ec->sid) free(ec->sid); + ec->sid = calloc(i, sizeof(uint16_t)); + memcpy(ec->sid, sid, i * sizeof(uint16_t)); + } + return save; +} + +/* Set names */ +int epggrab_channel_set_sname ( epggrab_channel_t *ec, const char **sname ) +{ + int save = 0, i = 0; + if ( !ec || !sname ) return 0; + if (!ec->sname) save = 1; + else { + while ( ec->sname[i] && sname[i] ) { + if (strcmp(ec->sname[i], sname[i])) break; + i++; + } + if (ec->sname[i] || sname[i]) save = 1; + } + if (save) { + if (ec->sname) { + i = 0; + while (ec->sname[i]) + free(ec->sname[i++]); + free(ec->sname); + } + i = 0; + while (sname[i++]); + ec->sname = calloc(i+1, sizeof(char*)); + i = 0; + while (sname[i]) { + ec->sname[i] = strdup(sname[i]); + i++; + } + } + return save; +} + +/* Channel settings updated */ +void epggrab_channel_updated ( epggrab_channel_t *ec ) +{ + channel_t *ch; + if (!ec) return; + + /* Find a link */ + if (!ec->channel) + RB_FOREACH(ch, &channel_name_tree, ch_name_link) + if (epggrab_channel_link(ec, ch)) break; + + /* Save */ + if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec); +} + +/* ID comparison */ +static int _ch_id_cmp ( void *a, void *b ) +{ + return strcmp(((epggrab_channel_t*)a)->id, + ((epggrab_channel_t*)b)->id); +} + +/* Find/Create channel in the list */ +epggrab_channel_t *epggrab_channel_find + ( epggrab_channel_tree_t *tree, const char *id, int create, int *save ) +{ + epggrab_channel_t *ec; + static epggrab_channel_t *skel = NULL; + if (!skel) skel = calloc(1, sizeof(epggrab_channel_t)); + skel->id = (char*)id; + + /* Find */ + if (!create) { + ec = RB_FIND(tree, skel, link, _ch_id_cmp); + + /* Find/Create */ + } else { + ec = RB_INSERT_SORTED(tree, skel, link, _ch_id_cmp); + if (!ec) { + ec = skel; + skel = NULL; + ec->id = strdup(id); + *save = 1; + } + } + return ec; +} + +/* ************************************************************************** + * Global routines + * *************************************************************************/ + +htsmsg_t *epggrab_channel_list ( void ) +{ + char name[100]; + epggrab_module_t *mod; + epggrab_channel_t *ec; + htsmsg_t *e, *m; + m = htsmsg_create_list(); + LIST_FOREACH(mod, &epggrab_modules, link) { + if (mod->enabled && mod->channels) { + RB_FOREACH(ec, mod->channels, link) { + e = htsmsg_create_map(); + htsmsg_add_str(e, "module", mod->id); + htsmsg_add_str(e, "id", ec->id); + sprintf(name, "%s: %s", mod->name, ec->name); + htsmsg_add_str(e, "name", name); + htsmsg_add_msg(m, NULL, e); + } + } + } + return m; +} + +void epggrab_channel_add ( channel_t *ch ) +{ + epggrab_module_t *m; + LIST_FOREACH(m, &epggrab_modules, link) { + if (m->ch_add) m->ch_add(m, ch); + } +} + +void epggrab_channel_rem ( channel_t *ch ) +{ + epggrab_module_t *m; + LIST_FOREACH(m, &epggrab_modules, link) { + if (m->ch_rem) m->ch_rem(m, ch); + } +} + +void epggrab_channel_mod ( channel_t *ch ) +{ + epggrab_module_t *m; + LIST_FOREACH(m, &epggrab_modules, link) { + if (m->ch_mod) m->ch_mod(m, ch); + } +} diff --git a/src/epggrab/eit.h b/src/epggrab/eit.h deleted file mode 100644 index c278718b..00000000 --- a/src/epggrab/eit.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Electronic Program Guide - eit grabber - * 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 EPGGRAB_EIT_H -#define EPGGRAB_EIT_H - -#include "epggrab.h" - -void eit_init ( epggrab_module_list_t *list ); -void eit_load ( void ); - -#endif diff --git a/src/epggrab/module.c b/src/epggrab/module.c new file mode 100644 index 00000000..e34f3272 --- /dev/null +++ b/src/epggrab/module.c @@ -0,0 +1,494 @@ +/* + * EPG Grabber - module 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 +#include +#include +#include +#include +#include + +#include "tvheadend.h" +#include "settings.h" +#include "htsmsg.h" +#include "htsmsg_xml.h" +#include "service.h" +#include "channels.h" +#include "spawn.h" +#include "file.h" +#include "epg.h" +#include "epggrab.h" +#include "epggrab/private.h" + +/* ************************************************************************** + * Module Access + * *************************************************************************/ + +epggrab_module_t* epggrab_module_find_by_id ( const char *id ) +{ + epggrab_module_t *m; + LIST_FOREACH(m, &epggrab_modules, link) { + if ( !strcmp(m->id, id) ) return m; + } + return NULL; +} + +htsmsg_t *epggrab_module_list ( void ) +{ + epggrab_module_t *m; + htsmsg_t *e, *a = htsmsg_create_list(); + LIST_FOREACH(m, &epggrab_modules, link) { + e = htsmsg_create_map(); + htsmsg_add_str(e, "id", m->id); + htsmsg_add_u32(e, "type", m->type); + htsmsg_add_u32(e, "enabled", m->enabled); + if(m->name) + htsmsg_add_str(e, "name", m->name); + if(m->type == EPGGRAB_EXT) { + epggrab_module_ext_t *ext = (epggrab_module_ext_t*)m; + htsmsg_add_str(e, "path", ext->path); + } + htsmsg_add_msg(a, NULL, e); + } + return a; +} + +/* ************************************************************************** + * Generic module routines + * *************************************************************************/ + +epggrab_module_t *epggrab_module_create + ( epggrab_module_t *skel, const char *id, const char *name, + epggrab_channel_tree_t *channels ) +{ + assert(skel); + + /* Setup */ + skel->id = strdup(id); + skel->name = strdup(name); + skel->channels = channels; + if (channels) { + skel->ch_save = epggrab_module_ch_save; + skel->ch_add = epggrab_module_ch_add; + skel->ch_mod = epggrab_module_ch_mod; + skel->ch_rem = epggrab_module_ch_rem; + } + + /* Insert */ + assert(!epggrab_module_find_by_id(id)); + LIST_INSERT_HEAD(&epggrab_modules, skel, link); + + return skel; +} + +/* + * Run the parse + */ +void epggrab_module_parse + ( void *m, htsmsg_t *data ) +{ + time_t tm1, tm2; + int save = 0; + epggrab_stats_t stats; + epggrab_module_int_t *mod = m; + + /* Parse */ + memset(&stats, 0, sizeof(stats)); + pthread_mutex_lock(&global_lock); + time(&tm1); + save |= mod->parse(mod, data, &stats); + time(&tm2); + if (save) epg_updated(); + pthread_mutex_unlock(&global_lock); + htsmsg_destroy(data); + + /* Debug stats */ + tvhlog(LOG_INFO, mod->id, "parse took %d seconds", tm2 - tm1); + tvhlog(LOG_INFO, mod->id, " channels tot=%5d new=%5d mod=%5d", + stats.channels.total, stats.channels.created, + stats.channels.modified); + tvhlog(LOG_INFO, mod->id, " brands tot=%5d new=%5d mod=%5d", + stats.brands.total, stats.brands.created, + stats.brands.modified); + tvhlog(LOG_INFO, mod->id, " seasons tot=%5d new=%5d mod=%5d", + stats.seasons.total, stats.seasons.created, + stats.seasons.modified); + tvhlog(LOG_INFO, mod->id, " episodes tot=%5d new=%5d mod=%5d", + stats.episodes.total, stats.episodes.created, + stats.episodes.modified); + tvhlog(LOG_INFO, mod->id, " broadcasts tot=%5d new=%5d mod=%5d", + stats.broadcasts.total, stats.broadcasts.created, + stats.broadcasts.modified); +} + +/* ************************************************************************** + * Module channel routines + * *************************************************************************/ + +void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch ) +{ + int i; + htsmsg_t *m = htsmsg_create_map(); + htsmsg_t *a; + epggrab_module_t *mod = _m; + + if (ch->name) + htsmsg_add_str(m, "name", ch->name); + if (ch->icon) + htsmsg_add_str(m, "icon", ch->icon); + if (ch->channel) + htsmsg_add_u32(m, "channel", ch->channel->ch_id); + if (ch->sid) { + a = htsmsg_create_list(); + i = 0; + while (ch->sid[i]) { + htsmsg_add_u32(a, NULL, ch->sid[i]); + i++; + } + htsmsg_add_msg(m, "sid", a); + } + if (ch->sname) { + a = htsmsg_create_list(); + i = 0; + while (ch->sname[i]) { + htsmsg_add_str(a, NULL, ch->sname[i]); + i++; + } + htsmsg_add_msg(m, "sname", a); + } + if (ch->number) + htsmsg_add_u32(m, "number", ch->number); + + hts_settings_save(m, "epggrab/%s/channels/%s", mod->id, ch->id); +} + +void epggrab_module_ch_add ( void *m, channel_t *ch ) +{ + epggrab_channel_t *egc; + epggrab_module_int_t *mod = m; + RB_FOREACH(egc, mod->channels, link) { + if (epggrab_channel_link(egc, ch)) { + if (mod->ch_save) mod->ch_save(mod, egc); + break; + } + } +} + +void epggrab_module_ch_rem ( void *m, channel_t *ch ) +{ + epggrab_channel_t *egc; + epggrab_module_int_t *mod = m; + RB_FOREACH(egc, mod->channels, link) { + if (egc->channel == ch) { + egc->channel = NULL; + if (mod->ch_save) mod->ch_save(mod, egc); + break; + } + } +} + +void epggrab_module_ch_mod ( void *mod, channel_t *ch ) +{ + return epggrab_module_ch_add(mod, ch); +} + +static void _epggrab_module_channel_load + ( epggrab_module_t *mod, htsmsg_t *m, const char *id ) +{ + int save = 0, i; + const char *str; + uint32_t u32; + htsmsg_t *a; + htsmsg_field_t *f; + + epggrab_channel_t *ch = epggrab_channel_find(mod->channels, id, 1, &save); + + if ((str = htsmsg_get_str(m, "name"))) + ch->name = strdup(str); + if ((str = htsmsg_get_str(m, "icon"))) + ch->icon = strdup(str); + if ((a = htsmsg_get_list(m, "sid"))) { + i = 0; + HTSMSG_FOREACH(f, a) i++; + if (i) { + ch->sid = calloc(i+1, sizeof(uint16_t)); + i = 0; + HTSMSG_FOREACH(f, a) { + ch->sid[i++] = (uint16_t)f->hmf_s64; + } + } + } + if ((a = htsmsg_get_list(m, "sname"))) { + i = 0; + HTSMSG_FOREACH(f, a) i++; + if (i) { + ch->sname = calloc(i+1, sizeof(char*)); + i = 0; + HTSMSG_FOREACH(f, a) { + ch->sname[i++] = strdup(f->hmf_str); + } + } + } + if(!htsmsg_get_u32(m, "number", &u32)) + ch->number = u32; + + if (!htsmsg_get_u32(m, "channel", &u32)) + ch->channel = channel_find_by_identifier(u32); +} + +void epggrab_module_channels_load ( epggrab_module_t *mod ) +{ + htsmsg_t *m, *e; + htsmsg_field_t *f; + if (!mod || !mod->channels) return; + if ((m = hts_settings_load("epggrab/%s/channels", mod->id))) { + HTSMSG_FOREACH(f, m) { + if ((e = htsmsg_get_map_by_field(f))) + _epggrab_module_channel_load(mod, e, f->hmf_name); + } + htsmsg_destroy(m); + } +} + +/* ************************************************************************** + * Internal module routines + * *************************************************************************/ + +epggrab_module_int_t *epggrab_module_int_create + ( epggrab_module_int_t *skel, + const char *id, const char *name, const char *path, + char* (*grab) (void*m), + int (*parse) (void *m, htsmsg_t *data, epggrab_stats_t *sta), + htsmsg_t* (*trans) (void *mod, char *data), + epggrab_channel_tree_t *channels ) +{ + /* Allocate data */ + if (!skel) skel = calloc(1, sizeof(epggrab_module_int_t)); + + /* Pass through */ + epggrab_module_create((epggrab_module_t*)skel, id, name, channels); + + /* Int data */ + skel->type = EPGGRAB_INT; + skel->path = strdup(path); + skel->channels = channels; + skel->grab = grab ?: epggrab_module_grab_spawn; + skel->trans = trans ?: epggrab_module_trans_xml; + skel->parse = parse; + + return skel; +} + +char *epggrab_module_grab_spawn ( void *m ) +{ + int outlen; + char *outbuf; + epggrab_module_int_t *mod = m; + + /* Debug */ + tvhlog(LOG_INFO, mod->id, "grab %s", mod->path); + + /* Grab */ + outlen = spawn_and_store_stdout(mod->path, NULL, &outbuf); + if ( outlen < 1 ) { + tvhlog(LOG_ERR, "pyepg", "no output detected"); + return NULL; + } + + return outbuf; +} + + + +htsmsg_t *epggrab_module_trans_xml ( void *m, char *c ) +{ + htsmsg_t *ret; + char errbuf[100]; + + if (!c) return NULL; + + /* Extract */ + ret = htsmsg_xml_deserialize(c, errbuf, sizeof(errbuf)); + if (!ret) + tvhlog(LOG_ERR, "pyepg", "htsmsg_xml_deserialize error %s", errbuf); + return ret; +} + +/* ************************************************************************** + * External module routines + * *************************************************************************/ + +/* + * Socket handler + */ +static void _epggrab_socket_handler ( epggrab_module_ext_t *mod, int s ) +{ + size_t outlen; + char *outbuf; + time_t tm1, tm2; + htsmsg_t *data = NULL; + + /* Grab/Translate */ + time(&tm1); + outlen = file_readall(s, &outbuf); + if (outlen) data = mod->trans(mod, outbuf); + time(&tm2); + + /* Process */ + if ( data ) { + tvhlog(LOG_INFO, mod->id, "grab took %d seconds", tm2 - tm1); + epggrab_module_parse(mod, data); + + /* Failed */ + } else { + tvhlog(LOG_ERR, mod->id, "failed to read data"); + } +} + + +/* + * External (socket) grab thread + */ +static void *_epggrab_socket_thread ( void *p ) +{ + int s; + epggrab_module_ext_t *mod = (epggrab_module_ext_t*)p; + tvhlog(LOG_INFO, mod->id, "external socket enabled"); + + while ( mod->enabled && mod->sock ) { + tvhlog(LOG_DEBUG, mod->id, "waiting for connection"); + s = accept(mod->sock, NULL, NULL); + if (s <= 0) continue; + tvhlog(LOG_DEBUG, mod->id, "got connection %d", s); + _epggrab_socket_handler(mod, s); + } + tvhlog(LOG_DEBUG, mod->id, "terminated"); + return NULL; +} + +/* + * Enable socket module + */ +int epggrab_module_enable_socket ( void *m, uint8_t e ) +{ + pthread_t tid; + pthread_attr_t tattr; + struct sockaddr_un addr; + epggrab_module_ext_t *mod = (epggrab_module_ext_t*)m; + assert(mod->type == EPGGRAB_EXT); + + /* Ignore */ + if ( mod->enabled == e ) return 0; + + /* Disable */ + if (!e) { + shutdown(mod->sock, SHUT_RDWR); + close(mod->sock); + unlink(mod->path); + mod->sock = 0; + + /* Enable */ + } else { + unlink(mod->path); // just in case! + + mod->sock = socket(AF_UNIX, SOCK_STREAM, 0); + assert(mod->sock); + + memset(&addr, 0, sizeof(struct sockaddr_un)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, mod->path, 100); + if ( bind(mod->sock, (struct sockaddr*)&addr, + sizeof(struct sockaddr_un)) != 0 ) { + tvhlog(LOG_ERR, mod->id, "failed to bind socket"); + close(mod->sock); + mod->sock = 0; + return 0; + } + + if ( listen(mod->sock, 5) != 0 ) { + tvhlog(LOG_ERR, mod->id, "failed to listen on socket"); + close(mod->sock); + mod->sock = 0; + return 0; + } + + tvhlog(LOG_DEBUG, mod->id, "starting socket thread"); + pthread_attr_init(&tattr); + pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); + pthread_create(&tid, &tattr, _epggrab_socket_thread, mod); + } + mod->enabled = e; + return 1; +} + +/* + * Create a module + */ +epggrab_module_ext_t *epggrab_module_ext_create + ( epggrab_module_ext_t *skel, + const char *id, const char *name, const char *sockid, + int (*parse) (void *m, htsmsg_t *data, epggrab_stats_t *sta), + htsmsg_t* (*trans) (void *mod, char *data), + epggrab_channel_tree_t *channels ) +{ + char path[512]; + + /* Allocate data */ + if (!skel) skel = calloc(1, sizeof(epggrab_module_ext_t)); + + /* Pass through */ + snprintf(path, 512, "%s/epggrab/%s.sock", + hts_settings_get_root(), sockid); + epggrab_module_int_create((epggrab_module_int_t*)skel, + id, name, path, + NULL, parse, trans, + channels); + + /* Local */ + skel->type = EPGGRAB_EXT; + skel->enable = epggrab_module_enable_socket; + + return skel; +} + +/* ************************************************************************** + * OTA module functions + * *************************************************************************/ + +epggrab_module_ota_t *epggrab_module_ota_create + ( epggrab_module_ota_t *skel, + const char *id, const char *name, + void (*start) (epggrab_module_ota_t*m, + struct th_dvb_mux_instance *tdmi), + int (*enable) (void *m, uint8_t e ), + epggrab_channel_tree_t *channels ) +{ + if (!skel) skel = calloc(1, sizeof(epggrab_module_ota_t)); + + /* Pass through */ + epggrab_module_create((epggrab_module_t*)skel, id, name, channels); + + /* Setup */ + skel->type = EPGGRAB_OTA; + skel->enable = enable; + skel->start = start; + TAILQ_INIT(&skel->muxes); + + return skel; +} + diff --git a/src/epggrab/eit.c b/src/epggrab/module/eit.c similarity index 81% rename from src/epggrab/eit.c rename to src/epggrab/module/eit.c index 5c182918..0f291ae0 100644 --- a/src/epggrab/eit.c +++ b/src/epggrab/module/eit.c @@ -23,9 +23,38 @@ #include "dvb/dvb_support.h" #include "service.h" #include "epg.h" -#include "epggrab/eit.h" +#include "epggrab.h" +#include "epggrab/private.h" -static epggrab_module_t _eit_mod; +/* ************************************************************************ + * Status handling + * ***********************************************************************/ + +typedef struct eit_status +{ + int tid; + int sec; +} eit_status_t; + +/* ************************************************************************ + * Diagnostics + * ***********************************************************************/ + +// Dump a descriptor tag for debug (looking for new tags etc...) +static void _eit_dtag_dump ( uint8_t dtag, uint8_t dlen, uint8_t *buf ) +{ + int i = 0, j = 0; + char tmp[100]; + tvhlog(LOG_DEBUG, "eit", "dtag 0x%02X len %d\n", dtag, dlen); + while (i < dlen) + while (dlen--) { + j += sprintf(tmp+j, "%02X ", buf[i]); + i++; + if ((i % 8) == 0 || !dlen) { + tvhlog(LOG_DEBUG, "eit", " %s", tmp); + } + } +} /* ************************************************************************ * Processing @@ -112,12 +141,13 @@ static int _eit_callback ( th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque ) { - th_dvb_mux_instance_t *otdmi = tdmi; + epggrab_ota_mux_t *ota = (epggrab_ota_mux_t*)opaque; th_dvb_adapter_t *tda; service_t *svc; channel_t *ch; epg_broadcast_t *ebc; epg_episode_t *ee; + eit_status_t *sta; int resched = 0, save = 0, save2 = 0, dllen, dtag, dlen; uint16_t tsid, sid, eid; uint8_t bw, hd, ws, ad, ds, st; @@ -129,14 +159,26 @@ static int _eit_callback char desc[5000]; htsmsg_t *extra; - /* Disabled */ - if(!_eit_mod.enabled) return 0; - /* Invalid */ if(tableid < 0x4e || tableid > 0x6f || len < 11) return -1; - /* Skip */ + /* Already complete */ + if (epggrab_ota_is_complete(ota)) return 0; + + /* Started */ + sta = ota->status; + if (epggrab_ota_begin(ota)) { + sta->tid = tableid; + sta->sec = ptr[3]; + + /* Complete */ + } else if (sta->tid == tableid && sta->sec == ptr[3]) { + epggrab_ota_complete(ota); + return 0; + } + + /* Don't process */ if((ptr[2] & 1) == 0) return 0; @@ -160,14 +202,17 @@ static int _eit_callback if (!svc || !svc->s_enabled || !(ch = svc->s_ch)) return 0; /* Ignore (disabled) */ + // TODO: should this be altered? if (!svc->s_dvb_eit_enable) return 0; - /* Register interest */ + /* Register as interesting */ + // TODO: do we want to register for now/next? + // TODO: want should the intervals be? if (tableid < 0x50) - epggrab_ota_register(&_eit_mod, otdmi, 20, 0); + epggrab_ota_register(ota, 20, 0); else - epggrab_ota_register(&_eit_mod, otdmi, 40, 0); - + epggrab_ota_register(ota, 40, 0); + /* Up to date */ #if TODO_INCLUDE_EIT_VER_CHECK ver = ptr[2] >> 1 & 0x1f; @@ -198,7 +243,7 @@ static int _eit_callback continue; } - /* Mark re-schedule detect */ + /* Mark re-schedule detect (only now/next) */ if (save2 && tableid < 0x50) resched = 1; /* Process tags */ @@ -298,6 +343,7 @@ static int _eit_callback /* Ignore */ default: + _eit_dtag_dump(dtag, dlen, ptr); break; } @@ -346,9 +392,8 @@ static int _eit_callback } /* Update EPG */ - if (resched) tvhlog(LOG_DEBUG, "eit", "alert grabbers to resched"); - // TODO: handle the reschedule - if (save) epg_updated(); + if (resched) epggrab_resched(); + if (save) epg_updated(); return 0; } @@ -357,19 +402,21 @@ static int _eit_callback * Module Setup * ***********************************************************************/ -static void _eit_tune ( epggrab_module_t *m, th_dvb_mux_instance_t *tdmi ) +static void _eit_start + ( epggrab_module_ota_t *m, th_dvb_mux_instance_t *tdmi ) { - if (_eit_mod.enabled) - tdt_add(tdmi, NULL, _eit_callback, NULL, "eit", TDT_CRC, 0x12, NULL); + epggrab_ota_mux_t *ota; + if (!m->enabled) return; + if (!(ota = epggrab_ota_create(m, tdmi))) return; + if (!ota->status) + ota->status = calloc(1, sizeof(eit_status_t)); + tdt_add(tdmi, NULL, _eit_callback, ota, "eit", TDT_CRC, 0x12, NULL); } -void eit_init ( epggrab_module_list_t *list ) +void eit_init ( void ) { - _eit_mod.id = strdup("eit"); - _eit_mod.name = strdup("EIT: On-Air Grabber"); - _eit_mod.tune = _eit_tune; - *((uint8_t*)&_eit_mod.flags) = EPGGRAB_MODULE_OTA; - LIST_INSERT_HEAD(list, &_eit_mod, link); + epggrab_module_ota_create(NULL, "eit", "EIT: DVB Grabber", + _eit_start, NULL, NULL); } void eit_load ( void ) diff --git a/src/epggrab/opentv.c b/src/epggrab/module/opentv.c similarity index 70% rename from src/epggrab/opentv.c rename to src/epggrab/module/opentv.c index 7e7ead3f..0ae4181a 100644 --- a/src/epggrab/opentv.c +++ b/src/epggrab/module/opentv.c @@ -25,8 +25,8 @@ #include "channels.h" #include "huffman.h" #include "epg.h" -#include "epggrab/ota.h" -#include "epggrab/opentv.h" +#include "epggrab.h" +#include "epggrab/private.h" #include "subscriptions.h" #include "streaming.h" #include "service.h" @@ -35,31 +35,82 @@ static epggrab_channel_tree_t _opentv_channels; -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 ); - /* ************************************************************************ - * Data structures + * Status monitoring * ***********************************************************************/ -#define OPENTV_SCAN_MAX 600 // 10min max scan period -#define OPENTV_SCAN_PER 3600 // 1hour interval +/* Internal event structure */ +typedef struct opentv_event +{ + RB_ENTRY(opentv_event) ev_link; ///< List of partial events + uint16_t cid; ///< Channel ID + uint16_t eid; ///< Events ID + time_t start; ///< Start time + time_t stop; ///< Event stop time + char *title; ///< Event title + char *summary; ///< Event summary + char *desc; ///< Event description + uint8_t cat; ///< Event category + uint16_t series; ///< Series (link) reference + + uint8_t type; ///< 0x1=title, 0x2=summary +} opentv_event_t; -/* Data carousel status */ +/* PID status (for event PIDs) */ +typedef struct opentv_pid +{ + LIST_ENTRY(opentv_pid) link; + int pid; + enum { + OPENTV_PID_INIT, + OPENTV_PID_STARTED, + OPENTV_PID_COMPLETE + } state; + uint8_t start[20]; +} opentv_pid_t; + +/* Scan status */ typedef struct opentv_status { - LIST_ENTRY(opentv_status) link; - int pid; - enum { - OPENTV_STA_INIT, - OPENTV_STA_STARTED, - OPENTV_STA_COMPLETE - } status; - uint8_t start[20]; + int begbat; + int endbat; + LIST_HEAD(, opentv_pid) pids; + RB_HEAD(, opentv_event) events; } opentv_status_t; +/* Get a pid entry */ +static opentv_pid_t *_opentv_status_get_pid + ( opentv_status_t *sta, int pid ) +{ + opentv_pid_t *p; + LIST_FOREACH(p, &sta->pids, link) { + if (p->pid == pid) break; + } + if (!p) { + p = calloc(1, sizeof(opentv_pid_t)); + p->pid = pid; + LIST_INSERT_HEAD(&sta->pids, p, link); + } + return p; +} + +/* Clear events */ +static void _opentv_status_remove_events ( opentv_status_t *sta ) +{ + opentv_event_t *ev; + while ((ev = RB_FIRST(&sta->events))) { + RB_REMOVE(&sta->events, ev, ev_link); + if (ev->title) free(ev->title); + if (ev->summary) free(ev->summary); + if (ev->desc) free(ev->desc); + free(ev); + } +} + +/* ************************************************************************ + * Module structure + * ***********************************************************************/ + /* Huffman dictionary */ typedef struct opentv_dict { @@ -71,7 +122,7 @@ typedef struct opentv_dict /* Provider configuration */ typedef struct opentv_module_t { - epggrab_module_t ; ///< Base struct + epggrab_module_ota_t ; ///< Base struct int nid; int tsid; @@ -80,10 +131,6 @@ typedef struct opentv_module_t int *title; int *summary; opentv_dict_t *dict; - - int endbat; - int begbat; - LIST_HEAD(, opentv_status) status; } opentv_module_t; @@ -104,151 +151,6 @@ static opentv_dict_t *_opentv_dict_find ( const char *id ) return RB_FIND(&_opentv_dicts, &skel, h_link, _dict_cmp); } -/* ************************************************************************ - * Module functions - * ***********************************************************************/ - -static opentv_status_t *_opentv_module_get_status - ( opentv_module_t *mod, int pid ) -{ - opentv_status_t *sta; - LIST_FOREACH(sta, &mod->status, link) - if (sta->pid == pid) break; - if (!sta) { - sta = calloc(1, sizeof(opentv_status_t)); - sta->pid = pid; - LIST_INSERT_HEAD(&mod->status, sta, link); - } - return sta; -} - -/* ************************************************************************ - * Configuration loading - * ***********************************************************************/ - -static int* _pid_list_to_array ( htsmsg_t *m, opentv_module_t *mod ) -{ - int i = 1; - int *ret; - htsmsg_field_t *f; - HTSMSG_FOREACH(f, m) - if (f->hmf_s64) i++; - ret = calloc(i, sizeof(int)); - i = 0; - HTSMSG_FOREACH(f, m) - if (f->hmf_s64) { - ret[i] = (int)f->hmf_s64; - if (mod) (void)_opentv_module_get_status(mod, ret[i]); - i++; - } - return ret; -} - -static int _opentv_dict_load_one ( const char *id, htsmsg_t *m ) -{ - opentv_dict_t *dict = calloc(1, sizeof(opentv_dict_t)); - dict->id = (char*)id; - if (RB_INSERT_SORTED(&_opentv_dicts, dict, h_link, _dict_cmp)) { - tvhlog(LOG_DEBUG, "opentv", "ignore duplicate dictionary %s", id); - free(dict); - return 0; - } else { - dict->codes = huffman_tree_build(m); - if (!dict->codes) { - RB_REMOVE(&_opentv_dicts, dict, h_link); - free(dict); - return -1; - } else { - dict->id = strdup(id); - return 1; - } - } -} - -static void _opentv_dict_load ( htsmsg_t *m ) -{ - int r; - htsmsg_t *e; - htsmsg_field_t *f; - HTSMSG_FOREACH(f, m) { - if ((e = htsmsg_get_list(m, f->hmf_name))) { - if ((r = _opentv_dict_load_one(f->hmf_name, e))) { - if (r > 0) - tvhlog(LOG_INFO, "opentv", "dictionary %s loaded", f->hmf_name); - else - tvhlog(LOG_WARNING, "opentv", "dictionary %s failed", f->hmf_name); - } - } - } - htsmsg_destroy(m); -} - -static int _opentv_prov_load_one - ( const char *id, htsmsg_t *m, epggrab_module_list_t *list ) -{ - char buf[100]; - htsmsg_t *cl, *tl, *sl; - uint32_t tsid, sid, nid; - const char *str, *name; - opentv_dict_t *dict; - static opentv_module_t *mod = NULL; - - /* Check config */ - if (!(name = htsmsg_get_str(m, "name"))) return -1; - if (!(str = htsmsg_get_str(m, "dict"))) return -1; - if (!(dict = _opentv_dict_find(str))) return -1; - if (!(cl = htsmsg_get_list(m, "channel"))) return -1; - if (!(tl = htsmsg_get_list(m, "title"))) return -1; - if (!(sl = htsmsg_get_list(m, "summary"))) return -1; - if (htsmsg_get_u32(m, "nid", &nid)) return -1; - if (htsmsg_get_u32(m, "tsid", &tsid)) return -1; - if (htsmsg_get_u32(m, "sid", &sid)) return -1; - - /* Exists */ - sprintf(buf, "opentv-%s", id); - if (epggrab_module_find_by_id(buf)) return 0; - - /* Create module */ - mod = calloc(1, sizeof(opentv_module_t)); - mod->id = strdup(buf); - sprintf(buf, "OpenTV: %s", name); - mod->name = strdup(buf); - mod->enable = _opentv_enable; - mod->tune = _opentv_tune; - mod->channels = &_opentv_channels; - *((uint8_t*)&mod->flags) = EPGGRAB_MODULE_OTA; - LIST_INSERT_HEAD(list, ((epggrab_module_t*)mod), link); - - /* Add provider details */ - mod->dict = dict; - mod->nid = nid; - mod->tsid = tsid; - mod->sid = sid; - mod->channel = _pid_list_to_array(cl, NULL); - mod->title = _pid_list_to_array(tl, mod); - mod->summary = _pid_list_to_array(sl, mod); - - return 1; -} - -static void _opentv_prov_load ( htsmsg_t *m, epggrab_module_list_t *list ) -{ - int r; - htsmsg_t *e; - htsmsg_field_t *f; - HTSMSG_FOREACH(f, m) { - if ((e = htsmsg_get_map_by_field(f))) { - if ((r = _opentv_prov_load_one(f->hmf_name, e, list))) { - if (r > 0) - tvhlog(LOG_INFO, "opentv", "provider %s loaded", f->hmf_name); - else - tvhlog(LOG_WARNING, "opentv", "provider %s failed", f->hmf_name); - } - } - } - htsmsg_destroy(m); -} - /* ************************************************************************ * EPG Object wrappers * ***********************************************************************/ @@ -258,7 +160,7 @@ static epggrab_channel_t *_opentv_find_epggrab_channel { char chid[32]; sprintf(chid, "%s-%d", mod->id, cid); - return epggrab_module_channel_find((epggrab_module_t*)mod, chid, create, save); + return epggrab_channel_find(&_opentv_channels, chid, create, save); } static epg_season_t *_opentv_find_season @@ -303,26 +205,6 @@ static channel_t *_opentv_find_channel ( int tsid, int sid ) #define OPENTV_TITLE 0x01 #define OPENTV_SUMMARY 0x02 -/* Internal event structure */ -typedef struct opentv_event -{ - RB_ENTRY(opentv_event) ev_link; ///< List of partial events - uint16_t cid; ///< Channel ID - uint16_t eid; ///< Events ID - time_t start; ///< Start time - time_t stop; ///< Event stop time - char *title; ///< Event title - char *summary; ///< Event summary - char *desc; ///< Event description - uint8_t cat; ///< Event category - uint16_t series; ///< Series (link) reference - - uint8_t type; ///< 0x1=title, 0x2=summary -} opentv_event_t; - -/* List of partial events */ -RB_HEAD(, opentv_event) _opentv_events; - /* Event list comparator */ static int _ev_cmp ( void *_a, void *_b ) { @@ -399,7 +281,8 @@ static int _opentv_parse_event_record /* Parse a specific event */ static int _opentv_parse_event - ( opentv_module_t *prov, uint8_t *buf, int len, int cid, time_t mjd, + ( opentv_module_t *prov, opentv_status_t *sta, + uint8_t *buf, int len, int cid, time_t mjd, opentv_event_t *ev, int type ) { int slen = ((int)buf[2] & 0xf << 8) | buf[3]; @@ -410,9 +293,9 @@ static int _opentv_parse_event ev->eid = ((uint16_t)buf[0] << 8) | buf[1]; /* Get existing summary */ - e = RB_FIND(&_opentv_events, ev, ev_link, _ev_cmp); + e = RB_FIND(&sta->events, ev, ev_link, _ev_cmp); if (e) { - RB_REMOVE(&_opentv_events, e, ev_link); + RB_REMOVE(&sta->events, e, ev_link); memcpy(ev, e, sizeof(opentv_event_t)); free(e); } @@ -427,7 +310,8 @@ static int _opentv_parse_event /* Parse an event section */ static int _opentv_parse_event_section - ( opentv_module_t *mod, uint8_t *buf, int len, int type ) + ( opentv_module_t *mod, opentv_status_t *sta, + uint8_t *buf, int len, int type ) { int i, cid, save = 0; time_t mjd; @@ -452,7 +336,7 @@ static int _opentv_parse_event_section i = 7; while (i < len) { memset(&ev, 0, sizeof(opentv_event_t)); - i += _opentv_parse_event(mod, buf+i, len-i, cid, mjd, &ev, type); + i += _opentv_parse_event(mod, sta, buf+i, len-i, cid, mjd, &ev, type); /* Find broadcast */ if (ev.type & OPENTV_TITLE) { @@ -465,7 +349,7 @@ static int _opentv_parse_event_section if (!ebc) { opentv_event_t *skel = malloc(sizeof(opentv_event_t)); memcpy(skel, &ev, sizeof(opentv_event_t)); - assert(!RB_INSERT_SORTED(&_opentv_events, skel, ev_link, _ev_cmp)); + assert(!RB_INSERT_SORTED(&sta->events, skel, ev_link, _ev_cmp)); continue; // don't want to free() anything } } @@ -561,7 +445,7 @@ static int _opentv_parse_ts_desc } static int _opentv_bat_section - ( opentv_module_t *mod, uint8_t *buf, int len ) + ( opentv_module_t *mod, opentv_status_t *sta, uint8_t *buf, int len ) { int i, r; int bdlen, tllen, tdlen; @@ -579,10 +463,10 @@ static int _opentv_bat_section // Note: this is NOT the most robust of approaches, but it works // most of the time i = 0x80000000 | (bid << 8) | sec; - if (!mod->begbat) { - mod->begbat = i; - } else if (mod->begbat == i) { - mod->endbat = 1; + if (!sta->begbat) { + sta->begbat = i; + } else if (sta->begbat == i) { + sta->endbat = 1; return 0; } @@ -621,71 +505,58 @@ static int _opentv_bat_section * Table Callbacks * ***********************************************************************/ -static opentv_module_t *_opentv_table_callback +static epggrab_ota_mux_t *_opentv_event_callback ( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p ) { th_dvb_table_t *tdt = (th_dvb_table_t*)p; - opentv_module_t *mod = (opentv_module_t*)tdt->tdt_opaque; - epggrab_ota_mux_t *ota; - opentv_status_t *sta; + epggrab_ota_mux_t *ota = tdt->tdt_opaque; + opentv_status_t *sta = ota->status; + opentv_pid_t *pid; - /* Ignore BAT not complete */ - if (!mod->endbat) return NULL; - /* Ignore (not enough data) */ if (len < 20) return NULL; - /* Register */ - ota = epggrab_ota_register((epggrab_module_t*)mod, tdmi, - OPENTV_SCAN_MAX, OPENTV_SCAN_PER); - if (!ota) return NULL; + /* Ignore (don't have BAT) */ + if (!sta->endbat) return NULL; /* Finished / Blocked */ if (epggrab_ota_is_complete(ota)) return NULL; - if (epggrab_ota_is_blocked(ota)) return NULL; /* Begin (reset state) */ if (epggrab_ota_begin(ota)) { - opentv_event_t *ev; /* Remove outstanding event data */ - while ((ev = RB_FIRST(&_opentv_events))) { - RB_REMOVE(&_opentv_events, ev, ev_link); - if (ev->title) free(ev->title); - if (ev->desc) free(ev->desc); - if (ev->summary) free(ev->summary); - free(ev); - } + _opentv_status_remove_events(sta); /* Reset status */ - LIST_FOREACH(sta, &mod->status, link) - sta->status = OPENTV_STA_INIT; + LIST_FOREACH(pid, &sta->pids, link) + pid->state = OPENTV_PID_INIT; } /* Insert/Find */ - sta = _opentv_module_get_status(mod, tdt->tdt_pid); + pid = _opentv_status_get_pid(sta, tdt->tdt_pid); /* Begin PID */ - if (sta->status == OPENTV_STA_INIT) { - sta->status = OPENTV_STA_STARTED; - memcpy(sta->start, buf, 20); - return mod; + if (pid->state == OPENTV_PID_INIT) { + pid->state = OPENTV_PID_STARTED; + memcpy(pid->start, buf, 20); + return ota; /* PID Already complete */ - } else if (sta->status == OPENTV_STA_COMPLETE) { + } else if (pid->state == OPENTV_PID_COMPLETE) { return NULL; /* End PID */ - } else if (!memcmp(sta->start, buf, 20)) { - sta->status = OPENTV_STA_COMPLETE; + } else if (!memcmp(pid->start, buf, 20)) { + pid->state = OPENTV_PID_COMPLETE; /* Check rest */ - LIST_FOREACH(sta, &mod->status, link) - if (sta->status != OPENTV_STA_COMPLETE) return mod; + LIST_FOREACH(pid, &sta->pids, link) + if (pid->state != OPENTV_PID_COMPLETE) return ota; /* PID in progress */ } else { - return mod; + return ota; } /* Mark complete */ @@ -697,106 +568,267 @@ static opentv_module_t *_opentv_table_callback static int _opentv_title_callback ( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p ) { - opentv_module_t *mod = _opentv_table_callback(tdmi, buf, len, tid, p); - if (mod) - return _opentv_parse_event_section(mod, buf, len, OPENTV_TITLE); + epggrab_ota_mux_t *ota = _opentv_event_callback(tdmi, buf, len, tid, p); + if (ota) + return _opentv_parse_event_section((opentv_module_t*)ota->grab, + (opentv_status_t*)ota->status, + buf, len, OPENTV_TITLE); return 0; } static int _opentv_summary_callback ( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p ) { - opentv_module_t *mod = _opentv_table_callback(tdmi, buf, len, tid, p); - if (mod) - return _opentv_parse_event_section(mod, buf, len, OPENTV_SUMMARY); + epggrab_ota_mux_t *ota = _opentv_event_callback(tdmi, buf, len, tid, p); + if (ota) + return _opentv_parse_event_section((opentv_module_t*)ota->grab, + (opentv_status_t*)ota->status, + buf, len, OPENTV_SUMMARY); return 0; } static int _opentv_channel_callback ( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p ) { - opentv_module_t *mod = (opentv_module_t*)p; - if (!mod->endbat) - return _opentv_bat_section(mod, buf, len); + epggrab_ota_mux_t *ota = (epggrab_ota_mux_t*)p; + opentv_status_t *sta = ota->status; + if (!sta->endbat) + return _opentv_bat_section((opentv_module_t*)ota->grab, sta, buf, len); return 0; } +/* ************************************************************************ + * Module callbacks + * ***********************************************************************/ + +static void _opentv_ota_destroy ( epggrab_ota_mux_t *ota ) +{ + opentv_status_t *sta = ota->status; + opentv_pid_t *pid; + + /* Empty the events */ + _opentv_status_remove_events(sta); + + /* Empty pids */ + while ((pid = LIST_FIRST(&sta->pids))) { + LIST_REMOVE(pid, link); + free(pid); + } + + /* Free the rest */ + free(sta); + free(ota); +} + +static void _opentv_start + ( epggrab_module_ota_t *m, th_dvb_mux_instance_t *tdmi ) +{ + int *t; + struct dmx_sct_filter_params *fp; + epggrab_ota_mux_t *ota; + opentv_module_t *mod = (opentv_module_t*)m; + opentv_status_t *sta; + + /* Ignore */ + if (!m->enabled) return; + if (mod->tsid != tdmi->tdmi_transport_stream_id) return; + + /* Create link */ + if (!(ota = epggrab_ota_create(m, tdmi))) return; + if (!ota->status) { + ota->status = calloc(1, sizeof(opentv_status_t)); + ota->destroy = _opentv_ota_destroy; + } + sta = ota->status; + sta->begbat = sta->endbat = 0; + + /* Register (just in case we missed it on enable somehow) */ + epggrab_ota_register(ota, 600, 3600); // 10min scan every hour + + /* Install tables */ + tvhlog(LOG_INFO, "opentv", "install provider %s tables", mod->id); + + /* Channels */ + t = mod->channel; + while (*t) { + fp = dvb_fparams_alloc(); + fp->filter.filter[0] = 0x4a; + fp->filter.mask[0] = 0xff; + // TODO: what about 0x46 (service description) + tdt_add(tdmi, fp, _opentv_channel_callback, ota, + m->id, TDT_CRC, *t++, NULL); + } + + /* Titles */ + t = mod->title; + while (*t) { + fp = dvb_fparams_alloc(); + fp->filter.filter[0] = 0xa0; + fp->filter.mask[0] = 0xfc; + _opentv_status_get_pid(sta, *t); + tdt_add(tdmi, fp, _opentv_title_callback, ota, + m->id, TDT_CRC | TDT_TDT, *t++, NULL); + } + + /* Summaries */ + t = mod->summary; + while (*t) { + fp = dvb_fparams_alloc(); + fp->filter.filter[0] = 0xa8; + fp->filter.mask[0] = 0xfc; + _opentv_status_get_pid(sta, *t); + tdt_add(tdmi, fp, _opentv_summary_callback, ota, + m->id, TDT_CRC | TDT_TDT, *t++, NULL); + } +} + +static int _opentv_enable ( void *m, uint8_t e ) +{ + opentv_module_t *mod = (opentv_module_t*)m; + + if (mod->enabled == e) return 0; + mod->enabled = e; + + /* Register interest */ + if (e) { + epggrab_ota_create_and_register_by_id((epggrab_module_ota_t*)mod, + mod->nid, mod->tsid, + 600, 3600); + /* Remove all links */ + } else { + epggrab_ota_destroy_by_module((epggrab_module_ota_t*)mod); + } + + return 1; +} + +/* ************************************************************************ + * Configuration + * ***********************************************************************/ + +static int* _pid_list_to_array ( htsmsg_t *m ) +{ + int i = 1; + int *ret; + htsmsg_field_t *f; + HTSMSG_FOREACH(f, m) + if (f->hmf_s64) i++; + ret = calloc(i, sizeof(int)); + i = 0; + HTSMSG_FOREACH(f, m) + if (f->hmf_s64) { + ret[i] = (int)f->hmf_s64; + i++; + } + return ret; +} + +static int _opentv_dict_load_one ( const char *id, htsmsg_t *m ) +{ + opentv_dict_t *dict = calloc(1, sizeof(opentv_dict_t)); + dict->id = (char*)id; + if (RB_INSERT_SORTED(&_opentv_dicts, dict, h_link, _dict_cmp)) { + tvhlog(LOG_DEBUG, "opentv", "ignore duplicate dictionary %s", id); + free(dict); + return 0; + } else { + dict->codes = huffman_tree_build(m); + if (!dict->codes) { + RB_REMOVE(&_opentv_dicts, dict, h_link); + free(dict); + return -1; + } else { + dict->id = strdup(id); + return 1; + } + } +} + +static void _opentv_dict_load ( htsmsg_t *m ) +{ + int r; + htsmsg_t *e; + htsmsg_field_t *f; + HTSMSG_FOREACH(f, m) { + if ((e = htsmsg_get_list(m, f->hmf_name))) { + if ((r = _opentv_dict_load_one(f->hmf_name, e))) { + if (r > 0) + tvhlog(LOG_INFO, "opentv", "dictionary %s loaded", f->hmf_name); + else + tvhlog(LOG_WARNING, "opentv", "dictionary %s failed", f->hmf_name); + } + } + } + htsmsg_destroy(m); +} + +static int _opentv_prov_load_one ( const char *id, htsmsg_t *m ) +{ + char ibuf[100], nbuf[1000]; + htsmsg_t *cl, *tl, *sl; + uint32_t tsid, sid, nid; + const char *str, *name; + opentv_dict_t *dict; + opentv_module_t *mod; + + /* Check config */ + if (!(name = htsmsg_get_str(m, "name"))) return -1; + if (!(str = htsmsg_get_str(m, "dict"))) return -1; + if (!(dict = _opentv_dict_find(str))) return -1; + if (!(cl = htsmsg_get_list(m, "channel"))) return -1; + if (!(tl = htsmsg_get_list(m, "title"))) return -1; + if (!(sl = htsmsg_get_list(m, "summary"))) return -1; + if (htsmsg_get_u32(m, "nid", &nid)) return -1; + if (htsmsg_get_u32(m, "tsid", &tsid)) return -1; + if (htsmsg_get_u32(m, "sid", &sid)) return -1; + + /* Exists (we expect some duplicates due to config layout) */ + sprintf(ibuf, "opentv-%s", id); + if (epggrab_module_find_by_id(ibuf)) return 0; + + /* Create */ + sprintf(nbuf, "OpenTV: %s", name); + mod = (opentv_module_t*) + epggrab_module_ota_create(calloc(1, sizeof(opentv_module_t)), + ibuf, nbuf, + _opentv_start, _opentv_enable, + NULL); + + /* Add provider details */ + mod->dict = dict; + mod->nid = nid; + mod->tsid = tsid; + mod->sid = sid; + mod->channel = _pid_list_to_array(cl); + mod->title = _pid_list_to_array(tl); + mod->summary = _pid_list_to_array(sl); + + return 1; +} + +static void _opentv_prov_load ( htsmsg_t *m ) +{ + int r; + htsmsg_t *e; + htsmsg_field_t *f; + HTSMSG_FOREACH(f, m) { + if ((e = htsmsg_get_map_by_field(f))) { + if ((r = _opentv_prov_load_one(f->hmf_name, e))) { + if (r > 0) + tvhlog(LOG_INFO, "opentv", "provider %s loaded", f->hmf_name); + else + tvhlog(LOG_WARNING, "opentv", "provider %s failed", f->hmf_name); + } + } + } + htsmsg_destroy(m); +} + /* ************************************************************************ * Module Setup * ***********************************************************************/ -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->tsid == tdmi->tdmi_transport_stream_id)) { - tvhlog(LOG_INFO, "opentv", "install provider %s tables", mod->id); - - /* Channels */ - t = mod->channel; - while (*t) { - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0x4a; - fp->filter.mask[0] = 0xff; - // TODO: what about 0x46 (service description) - tdt_add(tdmi, fp, _opentv_channel_callback, mod, - m->id, TDT_CRC, *t++, NULL); - } - - /* Titles */ - t = mod->title; - while (*t) { - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0xa0; - fp->filter.mask[0] = 0xfc; - tdt_add(tdmi, fp, _opentv_title_callback, mod, - m->id, TDT_CRC | TDT_TDT, *t++, NULL); - } - - /* Summaries */ - t = mod->summary; - while (*t) { - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0xa8; - fp->filter.mask[0] = 0xfc; - tdt_add(tdmi, fp, _opentv_summary_callback, mod, - m->id, TDT_CRC | TDT_TDT, *t++, NULL); - } - - /* Must rebuild the BAT */ - mod->begbat = mod->endbat = 0; - } -} - -static int _opentv_enable ( epggrab_module_t *m, uint8_t e ) -{ - th_dvb_adapter_t *tda; - th_dvb_mux_instance_t *tdmi; - opentv_module_t *mod = (opentv_module_t*)m; - - 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->tsid) continue; - if (e) { - epggrab_ota_register(m, tdmi, OPENTV_SCAN_MAX, OPENTV_SCAN_PER); - } else { - epggrab_ota_unregister_one(m, tdmi); - } - } - } - - return 1; -} - -void opentv_init ( epggrab_module_list_t *list ) +void opentv_init ( void ) { htsmsg_t *m; const char *dr = tvheadend_dataroot(); @@ -804,15 +836,15 @@ void opentv_init ( epggrab_module_list_t *list ) /* Load dictionaries */ if ((m = hts_settings_load("epggrab/opentv/dict"))) _opentv_dict_load(m); - if ((m = hts_settings_load("%s/data/epggrab/opentv/dict", dr))) + if ((m = hts_settings_load("%sdata/epggrab/opentv/dict", dr))) _opentv_dict_load(m); tvhlog(LOG_INFO, "opentv", "dictonaries loaded"); /* Load providers */ if ((m = hts_settings_load("epggrab/opentv/prov"))) - _opentv_prov_load(m, list); - if ((m = hts_settings_load("%s/data/epggrab/opentv/prov", dr))) - _opentv_prov_load(m, list); + _opentv_prov_load(m); + if ((m = hts_settings_load("%sdata/epggrab/opentv/prov", dr))) + _opentv_prov_load(m); tvhlog(LOG_INFO, "opentv", "providers loaded"); } diff --git a/src/epggrab/pyepg.c b/src/epggrab/module/pyepg.c similarity index 87% rename from src/epggrab/pyepg.c rename to src/epggrab/module/pyepg.c index 8207aa4b..4dc7268a 100644 --- a/src/epggrab/pyepg.c +++ b/src/epggrab/module/pyepg.c @@ -24,18 +24,17 @@ #include "htsmsg_xml.h" #include "tvheadend.h" #include "spawn.h" -#include "epg.h" -#include "epggrab/pyepg.h" -#include "epggrab/xmltv.h" #include "channels.h" +#include "epg.h" +#include "epggrab.h" +#include "epggrab/private.h" static epggrab_channel_tree_t _pyepg_channels; -static epggrab_module_t *_pyepg_module; static epggrab_channel_t *_pyepg_channel_find ( const char *id, int create, int *save ) { - return epggrab_module_channel_find(_pyepg_module, id, create, save); + return epggrab_channel_find(&_pyepg_channels, id, create, save); } /* ************************************************************************** @@ -69,7 +68,7 @@ static int _pyepg_parse_channel ( htsmsg_t *data, epggrab_stats_t *stats ) const char *str; uint32_t u32; const char *sname[11]; - uint16_t sid[10]; + uint16_t sid[11]; int sid_idx = 0, sname_idx = 0; if ( data == NULL ) return 0; @@ -108,7 +107,10 @@ static int _pyepg_parse_channel ( htsmsg_t *data, epggrab_stats_t *stats ) } } } - if (sid_idx) save |= epggrab_channel_set_sid(ch, sid, sid_idx); + if (sid_idx) { + sid[sid_idx] = 0; + save |= epggrab_channel_set_sid(ch, sid); + } if (sname_idx) { sname[sname_idx] = NULL; save |= epggrab_channel_set_sname(ch, sname); @@ -412,14 +414,8 @@ static int _pyepg_parse_epg ( htsmsg_t *data, epggrab_stats_t *stats ) return save; } - -/* ************************************************************************ - * Module Setup - * ***********************************************************************/ - - static int _pyepg_parse - ( epggrab_module_t *mod, htsmsg_t *data, epggrab_stats_t *stats ) + ( void *mod, htsmsg_t *data, epggrab_stats_t *stats ) { htsmsg_t *tags, *epg; @@ -432,40 +428,23 @@ static int _pyepg_parse return 0; } -void pyepg_init ( epggrab_module_list_t *list ) -{ - epggrab_module_t *mod; +/* ************************************************************************ + * Module Setup + * ***********************************************************************/ - /* Standard module */ - mod = calloc(1, sizeof(epggrab_module_t)); - mod->id = strdup("pyepg"); - mod->path = strdup("/usr/bin/pyepg"); - mod->name = strdup("PyEPG"); - mod->grab = epggrab_module_grab; - mod->trans = epggrab_module_trans_xml; - mod->parse = _pyepg_parse; - mod->channels = &_pyepg_channels; - mod->ch_add = epggrab_module_channel_add; - mod->ch_rem = epggrab_module_channel_rem; - mod->ch_mod = epggrab_module_channel_mod; - *((uint8_t*)&mod->flags) = EPGGRAB_MODULE_INTERNAL; - LIST_INSERT_HEAD(list, mod, link); - _pyepg_module = mod; +void pyepg_init ( void ) +{ + /* Internal module */ + epggrab_module_int_create(NULL, "/usr/bin/pyepg", "PyEPG", "/usr/bin/pyepg", + NULL, _pyepg_parse, NULL, NULL); /* External module */ - mod = calloc(1, sizeof(epggrab_module_t)); - mod->id = strdup("pyepg_ext"); - mod->path = epggrab_module_socket_path("pyepg"); - mod->name = strdup("PyEPG"); - mod->channels = &_pyepg_channels; - mod->enable = epggrab_module_enable_socket; - mod->trans = epggrab_module_trans_xml; - mod->parse = _pyepg_parse; - *((uint8_t*)&mod->flags) = EPGGRAB_MODULE_EXTERNAL; - LIST_INSERT_HEAD(list, mod, link); + epggrab_module_ext_create(NULL, "pyepg", "PyEPG", "pyepg", + _pyepg_parse, NULL, + &_pyepg_channels); } void pyepg_load ( void ) { - epggrab_module_channels_load(_pyepg_module); + epggrab_module_channels_load(epggrab_module_find_by_id("pyepg")); } diff --git a/src/epggrab/xmltv.c b/src/epggrab/module/xmltv.c similarity index 87% rename from src/epggrab/xmltv.c rename to src/epggrab/module/xmltv.c index 442a4993..546f367a 100644 --- a/src/epggrab/xmltv.c +++ b/src/epggrab/module/xmltv.c @@ -31,22 +31,22 @@ #include "tvheadend.h" #include "channels.h" -#include "epg.h" -#include "xmltv.h" #include "spawn.h" +#include "epg.h" +#include "epggrab.h" +#include "epggrab/private.h" + #define XMLTV_FIND_GRABBERS "/usr/bin/tv_find_grabbers" static epggrab_channel_tree_t _xmltv_channels; -static epggrab_module_t *_xmltv_module; static epggrab_channel_t *_xmltv_channel_find ( const char *id, int create, int *save ) { - return epggrab_module_channel_find(_xmltv_module, id, create, save); + return epggrab_channel_find(&_xmltv_channels, id, create, save); } - /* ************************************************************************** * Parsing * *************************************************************************/ @@ -442,12 +442,8 @@ _xmltv_parse_tv(htsmsg_t *body, epggrab_stats_t *stats) return save; } -/* ************************************************************************ - * Module Setup - * ***********************************************************************/ - static int _xmltv_parse - ( epggrab_module_t *mod, htsmsg_t *data, epggrab_stats_t *stats ) + ( void *mod, htsmsg_t *data, epggrab_stats_t *stats ) { htsmsg_t *tags, *tv; @@ -460,17 +456,20 @@ static int _xmltv_parse return _xmltv_parse_tv(tv, stats); } -static void _xmltv_load_grabbers ( epggrab_module_list_t *list ) +/* ************************************************************************ + * Module Setup + * ***********************************************************************/ + +static void _xmltv_load_grabbers ( void ) { size_t i, outlen, p, n; char *outbuf; - char errbuf[100]; - epggrab_module_t *mod; + char name[1000]; /* Load data */ outlen = spawn_and_store_stdout(XMLTV_FIND_GRABBERS, NULL, &outbuf); if ( outlen < 1 ) { - tvhlog(LOG_ERR, "xmltv", "%s failed [%s]", XMLTV_FIND_GRABBERS, errbuf); + tvhlog(LOG_ERR, "xmltv", "%s failed", XMLTV_FIND_GRABBERS); return; } @@ -479,16 +478,9 @@ static void _xmltv_load_grabbers ( epggrab_module_list_t *list ) while ( i < outlen ) { if ( outbuf[i] == '\n' || outbuf[i] == '\0' ) { outbuf[i] = '\0'; - mod = calloc(1, sizeof(epggrab_module_t)); - mod->id = mod->path = strdup(&outbuf[p]); - mod->name = malloc(200); - mod->channels = &_xmltv_channels; - sprintf((char*)mod->name, "XMLTV: %s", &outbuf[n]); - *((uint8_t*)&mod->flags) = EPGGRAB_MODULE_INTERNAL; - mod->grab = epggrab_module_grab; - mod->trans = epggrab_module_trans_xml; - mod->parse = _xmltv_parse; - LIST_INSERT_HEAD(list, mod, link); + sprintf(name, "XMLTV: %s", &outbuf[n]); + epggrab_module_int_create(NULL, &outbuf[p], name, &outbuf[p], + NULL, _xmltv_parse, NULL, NULL); p = n = i + 1; } else if ( outbuf[i] == '|' ) { outbuf[i] = '\0'; @@ -499,31 +491,18 @@ static void _xmltv_load_grabbers ( epggrab_module_list_t *list ) free(outbuf); } -void xmltv_init ( epggrab_module_list_t *list ) +void xmltv_init ( void ) { - epggrab_module_t *mod; - /* External module */ - mod = calloc(1, sizeof(epggrab_module_t)); - mod->id = strdup("xmltv"); - mod->name = strdup("XMLTV"); - mod->path = epggrab_module_socket_path("xmltv"); - mod->enable = epggrab_module_enable_socket; - mod->trans = epggrab_module_trans_xml; - mod->parse = _xmltv_parse; - mod->channels = &_xmltv_channels; - mod->ch_add = epggrab_module_channel_add; - mod->ch_rem = epggrab_module_channel_rem; - mod->ch_mod = epggrab_module_channel_mod; - *((uint8_t*)&mod->flags) = EPGGRAB_MODULE_EXTERNAL; - LIST_INSERT_HEAD(list, mod, link); - _xmltv_module = mod; + epggrab_module_ext_create(NULL, "xmltv", "XMLTV", "xmltv", + _xmltv_parse, NULL, + &_xmltv_channels); /* Standard modules */ - _xmltv_load_grabbers(list); + _xmltv_load_grabbers(); } void xmltv_load ( void ) { - epggrab_module_channels_load(_xmltv_module); + epggrab_module_channels_load(epggrab_module_find_by_id("xmltv")); } diff --git a/src/epggrab/opentv.h b/src/epggrab/opentv.h deleted file mode 100644 index e46525d9..00000000 --- a/src/epggrab/opentv.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Electronic Program Guide - opentv - * 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_OPENTV_H__ -#define __TVH_EPGGRAB_OPENTV_H__ - -#include "dvb/dvb.h" -#include "epggrab.h" - -void opentv_tune ( th_dvb_mux_instance_t *tdmi ); -void opentv_init ( epggrab_module_list_t *list ); -void opentv_load ( void ); - -#endif /* __TVH_EPGGRAB_OPENTV_H__ */ diff --git a/src/epggrab/ota.c b/src/epggrab/ota.c deleted file mode 100644 index 2dc132d5..00000000 --- a/src/epggrab/ota.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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) { - ota->timeout = MAX(ota->timeout, timeout); - ota->interval = MIN(ota->interval, interval); - 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); - - /* Re-assign to correct queue */ - if (tdmi->tdmi_scan_queue) - TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); - dvb_mux_add_to_scan_queue(tdmi); - - 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_IDLE) { - 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); -} - -int epggrab_ota_is_complete ( epggrab_ota_mux_t *ota ) -{ - return ota->status == EPGGRAB_OTA_COMPLETED; -} - -int epggrab_ota_is_blocked ( epggrab_ota_mux_t *ota ) -{ - time_t t; - time(&t); - return t < (ota->completed + ota->interval); -} diff --git a/src/epggrab/ota.h b/src/epggrab/ota.h deleted file mode 100644 index f1220906..00000000 --- a/src/epggrab/ota.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 ); -int epggrab_ota_is_complete ( epggrab_ota_mux_t *ota ); -int epggrab_ota_is_blocked ( epggrab_ota_mux_t *ota ); - -#endif /* __TVH_EPGGRAB_OTA_H__ */ diff --git a/src/epggrab/otamux.c b/src/epggrab/otamux.c new file mode 100644 index 00000000..7a920266 --- /dev/null +++ b/src/epggrab/otamux.c @@ -0,0 +1,311 @@ +/* + * 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 . + */ + +/* + * TODO: currently I don't try to block multiple scans of the same + * tdmi on different adapters + */ + +#include "tvheadend.h" +#include "queue.h" +#include "epg.h" +#include "dvb/dvb.h" +#include "epggrab.h" +#include "epggrab/private.h" + +TAILQ_HEAD(, epggrab_ota_mux) ota_mux_all; + +/* ************************************************************************** + * Global functions (called from DVB code) + * *************************************************************************/ + +void epggrab_mux_start ( th_dvb_mux_instance_t *tdmi ) +{ + epggrab_module_t *m; + LIST_FOREACH(m, &epggrab_modules, link) { + if (m->type == EPGGRAB_OTA) { + ((epggrab_module_ota_t*)m)->start((epggrab_module_ota_t*)m, tdmi); + } + } +} + +void epggrab_mux_stop ( th_dvb_mux_instance_t *tdmi, int timeout ) +{ + // Note: the slightly akward list iteration here is because + // _ota_cancel/delete can remove the object and free() it + epggrab_ota_mux_t *a, *b; + a = TAILQ_FIRST(&tdmi->tdmi_epg_grab); + while (a) { + b = TAILQ_NEXT(a, tdmi_link); + if (a->tdmi == tdmi) { + if (timeout) + epggrab_ota_timeout(a); + else + epggrab_ota_cancel(a); + } + a = b; + } +} + +void epggrab_mux_delete ( th_dvb_mux_instance_t *tdmi ) +{ + epggrab_ota_destroy_by_tdmi(tdmi); +} + +int epggrab_mux_period ( th_dvb_mux_instance_t *tdmi ) +{ + int period = 0; + epggrab_ota_mux_t *ota; + TAILQ_FOREACH(ota, &tdmi->tdmi_epg_grab, tdmi_link) { + if (!ota->is_reg) continue; + if (ota->timeout > period) + period = ota->timeout; + } + return period; +} + +th_dvb_mux_instance_t *epggrab_mux_next ( th_dvb_adapter_t *tda ) +{ + time_t now; + epggrab_ota_mux_t *ota; + time(&now); + TAILQ_FOREACH(ota, &ota_mux_all, glob_link) { + if (ota->interval + ota->completed > now) return NULL; + if (!ota->is_reg) return NULL; + if (ota->tdmi->tdmi_adapter == tda) break; + } + return ota ? ota->tdmi : NULL; +} + +/* ************************************************************************** + * OTA Mux link functions + * *************************************************************************/ + +/* + * Comprison of ota_mux instances based on when they can next run + * + * Note: ordering isn't garaunteed to be perfect as we use the current time + */ +static int _ota_time_cmp ( void *_a, void *_b ) +{ + int r; + time_t now, wa, wb; + time(&now); + epggrab_ota_mux_t *a = _a; + epggrab_ota_mux_t *b = _b; + + /* Unreg'd always at the end */ + r = a->is_reg - b->is_reg; + if (r) return r; + + /* Check when */ + wa = a->completed + a->interval; + wb = b->completed + b->interval; + if (wa < now && wb < now) + return a->started - b->started; + else + return wa - wb; +} + +/* + * Create (temporary) or Find (existing) link + */ +epggrab_ota_mux_t *epggrab_ota_create + ( epggrab_module_ota_t *mod, th_dvb_mux_instance_t *tdmi ) +{ + /* Search for existing */ + epggrab_ota_mux_t *ota; + TAILQ_FOREACH(ota, &ota_mux_all, glob_link) { + if (ota->grab == mod && ota->tdmi == tdmi) break; + } + + /* Create new */ + if (!ota) { + ota = calloc(1, sizeof(epggrab_ota_mux_t)); + ota->grab = mod; + ota->tdmi = tdmi; + TAILQ_INSERT_TAIL(&ota_mux_all, ota, glob_link); + TAILQ_INSERT_TAIL(&tdmi->tdmi_epg_grab, ota, tdmi_link); + TAILQ_INSERT_TAIL(&mod->muxes, ota, grab_link); + + } else { + time_t now; + time(&now); + ota->state = EPGGRAB_OTA_MUX_IDLE; + + /* Blocked */ + if (epggrab_ota_is_blocked(ota)) ota = NULL; + } + return ota; +} + +/* + * Create and register using mux ID + */ +void epggrab_ota_create_and_register_by_id + ( epggrab_module_ota_t *mod, int nid, int tsid, int period, int interval ) +{ + th_dvb_adapter_t *tda; + th_dvb_mux_instance_t *tdmi; + epggrab_ota_mux_t *ota; + TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { + //TODO: if (tda->nitoid != nid) continue; + LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) { + if (tdmi->tdmi_transport_stream_id != tsid) continue; + ota = epggrab_ota_create(mod, tdmi); + epggrab_ota_register(ota, period, interval); + } + } +} + +/* + * Destrory link (either because it was temporary OR mux deleted) + */ +void epggrab_ota_destroy ( epggrab_ota_mux_t *ota ) +{ + TAILQ_REMOVE(&ota_mux_all, ota, glob_link); + TAILQ_REMOVE(&ota->tdmi->tdmi_epg_grab, ota, tdmi_link); + TAILQ_REMOVE(&ota->grab->muxes, ota, grab_link); + if (ota->destroy) ota->destroy(ota); + else { + if (ota->status) free(ota->status); + free(ota); + } +} + +/* + * Destroy by tdmi + */ +void epggrab_ota_destroy_by_tdmi ( th_dvb_mux_instance_t *tdmi ) +{ + epggrab_ota_mux_t *ota; + while ((ota = TAILQ_FIRST(&tdmi->tdmi_epg_grab))) + epggrab_ota_destroy(ota); +} + +/* + * Destroy by module + */ +void epggrab_ota_destroy_by_module ( epggrab_module_ota_t *mod ) +{ + epggrab_ota_mux_t *ota; + while ((ota = TAILQ_FIRST(&mod->muxes))) + epggrab_ota_destroy(ota); +} + +/* + * Register interest (called when useful data exists on the MUX + * thus inserting it into the EPG scanning queue + */ +void epggrab_ota_register ( epggrab_ota_mux_t *ota, int timeout, int interval ) +{ + int up = 0; + ota->is_reg = 1; + if (timeout > ota->timeout) { + up = 1; + ota->timeout = timeout; + } + if (interval > ota->interval) { + up = 1; + ota->interval = interval; + } + + if (up) { + TAILQ_REMOVE(&ota_mux_all, ota, glob_link); + TAILQ_INSERT_SORTED(&ota_mux_all, ota, glob_link, _ota_time_cmp); + } +} + +/* + * State changes + */ + +static void _epggrab_ota_finished ( epggrab_ota_mux_t *ota ) +{ + /* Temporary link - delete it */ + if (!ota->is_reg) + epggrab_ota_destroy(ota); + + /* Reinsert into reg queue */ + else { + TAILQ_REMOVE(&ota_mux_all, ota, glob_link); + TAILQ_INSERT_SORTED(&ota_mux_all, ota, glob_link, _ota_time_cmp); + } +} + +int epggrab_ota_begin ( epggrab_ota_mux_t *ota ) +{ + if (ota->state == EPGGRAB_OTA_MUX_IDLE) { + ota->state = EPGGRAB_OTA_MUX_RUNNING; + time(&ota->started); + return 1; + } + return 0; +} + +void epggrab_ota_complete ( epggrab_ota_mux_t *ota ) +{ + th_dvb_mux_instance_t *tdmi = ota->tdmi; + + if (ota->state != EPGGRAB_OTA_MUX_COMPLETE) { + ota->state = EPGGRAB_OTA_MUX_COMPLETE; + time(&ota->completed); + _epggrab_ota_finished(ota); + + /* Check others */ + TAILQ_FOREACH(ota, &tdmi->tdmi_epg_grab, tdmi_link) { + if (ota->is_reg && ota->state == EPGGRAB_OTA_MUX_RUNNING) break; + } + + /* All complete (bring timer forward) */ + if (!ota) { + gtimer_arm(&tdmi->tdmi_adapter->tda_mux_scanner_timer, + dvb_adapter_mux_scanner, tdmi->tdmi_adapter, 20); + } + } +} + +/* Reset */ +void epggrab_ota_cancel ( epggrab_ota_mux_t *ota ) +{ + ota->state = EPGGRAB_OTA_MUX_IDLE; + _epggrab_ota_finished(ota); +} + +/* Same as complete */ +void epggrab_ota_timeout ( epggrab_ota_mux_t *ota ) +{ + epggrab_ota_complete(ota); +} + +/* + * Check status + */ + +int epggrab_ota_is_complete ( epggrab_ota_mux_t *ota ) +{ + return ota->state == EPGGRAB_OTA_MUX_COMPLETE || + ota->state == EPGGRAB_OTA_MUX_TIMEDOUT; +} + +int epggrab_ota_is_blocked ( epggrab_ota_mux_t *ota ) +{ + time_t t; + time(&t); + return t < (ota->completed + ota->interval); +} diff --git a/src/epggrab/private.h b/src/epggrab/private.h new file mode 100644 index 00000000..ed1942d9 --- /dev/null +++ b/src/epggrab/private.h @@ -0,0 +1,154 @@ +/* + * EPG Grabber - private routines + * 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 __EPGGRAB_PRIVATE_H__ +#define __EPGGRAB_PRIVATE_H__ + +/* ************************************************************************** + * Generic module routines + * *************************************************************************/ + +epggrab_module_t *epggrab_module_create + ( epggrab_module_t *skel, + const char *id, const char *name, epggrab_channel_tree_t *channels ); + +char *epggrab_module_grab_spawn ( void *m ); +htsmsg_t *epggrab_module_trans_xml ( void *m, char *data ); + +void epggrab_module_ch_add ( void *m, struct channel *ch ); +void epggrab_module_ch_rem ( void *m, struct channel *ch ); +void epggrab_module_ch_mod ( void *m, struct channel *ch ); +void epggrab_module_ch_save ( void *m, epggrab_channel_t *ec ); + +int epggrab_module_enable_socket ( void *m, uint8_t e ); + +void epggrab_module_parse ( void *m, htsmsg_t *data ); + +void epggrab_module_channels_load ( epggrab_module_t *m ); + +/* ************************************************************************** + * Channel processing + * *************************************************************************/ + +int epggrab_channel_link ( epggrab_channel_t *ec, struct channel *ch ); + +epggrab_channel_t *epggrab_channel_find + ( epggrab_channel_tree_t *chs, const char *id, int create, int *save ); + +/* ************************************************************************** + * Internal module routines + * *************************************************************************/ + +epggrab_module_int_t *epggrab_module_int_create + ( epggrab_module_int_t *skel, + const char *id, const char *name, const char *path, + char* (*grab) (void*m), + int (*parse) (void *m, htsmsg_t *data, epggrab_stats_t *sta), + htsmsg_t* (*trans) (void *mod, char *data), + epggrab_channel_tree_t *channels ); + +/* ************************************************************************** + * External module routines + * *************************************************************************/ + +epggrab_module_ext_t *epggrab_module_ext_create + ( epggrab_module_ext_t *skel, + const char *id, const char *name, const char *sockid, + int (*parse) (void *m, htsmsg_t *data, epggrab_stats_t *sta), + htsmsg_t* (*trans) (void *mod, char *data), + epggrab_channel_tree_t *channels ); + +/* ************************************************************************** + * OTA module routines + * *************************************************************************/ + +epggrab_module_ota_t *epggrab_module_ota_create + ( epggrab_module_ota_t *skel, + const char *id, const char *name, + void (*start) (epggrab_module_ota_t*m, + struct th_dvb_mux_instance *tdmi), + int (*enable) (void *m, uint8_t e ), + epggrab_channel_tree_t *channels ); + +/* ************************************************************************** + * OTA mux link routines + * *************************************************************************/ + +/* + * Create/Find a link (unregistered) + * + * Note: this will return NULL for an already existing link that is currently + * blocked (i.e. has completed within interval period) + */ +epggrab_ota_mux_t *epggrab_ota_create + ( epggrab_module_ota_t *mod, struct th_dvb_mux_instance *tdmi ); +void epggrab_ota_create_and_register_by_id + ( epggrab_module_ota_t *mod, int nid, int tsid, + int period, int interval ); + +/* + * Delete + */ +void epggrab_ota_destroy ( epggrab_ota_mux_t *ota ); +void epggrab_ota_destroy_by_module ( epggrab_module_ota_t *mod ); +void epggrab_ota_destroy_by_tdmi ( struct th_dvb_mux_instance *tdmi ); + +/* + * Register interest + */ +void epggrab_ota_register + ( epggrab_ota_mux_t *ota, int timeout, int interval ); + +/* + * State change + */ +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 ); + +/* + * Status + */ +int epggrab_ota_is_complete ( epggrab_ota_mux_t *ota ); +int epggrab_ota_is_blocked ( epggrab_ota_mux_t *ota ); + +/* ************************************************************************** + * Module setup(s) + * *************************************************************************/ + +/* EIT module */ +void eit_init ( void ); +void eit_load ( void ); + +/* OpenTV module */ +void opentv_init ( void ); +void opentv_load ( void ); + +/* PyEPG module */ +void pyepg_init ( void ); +void pyepg_load ( void ); + +/* XMLTV module */ +void xmltv_init ( void ); +void xmltv_load ( void ); + +/* Note: this is reused by pyepg since they share a common format */ +int xmltv_parse_accessibility ( epg_broadcast_t *ebc, htsmsg_t *m ); + +#endif /* __EPGGRAB_PRIVATE_H__ */ diff --git a/src/epggrab/pyepg.h b/src/epggrab/pyepg.h deleted file mode 100644 index 9686c784..00000000 --- a/src/epggrab/pyepg.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Electronic Program Guide - pyepg grabber - * 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 EPGGRAB_PYEPG_H -#define EPGGRAB_PYEPG_H - -#include "epggrab.h" - -void pyepg_init ( epggrab_module_list_t *list ); -void pyepg_load ( void ); - -#endif diff --git a/src/epggrab/xmltv.h b/src/epggrab/xmltv.h deleted file mode 100644 index 104842a7..00000000 --- a/src/epggrab/xmltv.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Electronic Program Guide - xmltv grabber - * 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 EPGGRAB_XMLTV_H -#define EPGGRAB_XMLTV_H - -#include "htsmsg.h" -#include "epggrab.h" - -void xmltv_init ( epggrab_module_list_t *list ); -void xmltv_load ( void ); - -// reused by pyepg -int xmltv_parse_accessibility ( epg_broadcast_t *ebc, htsmsg_t *m ); - -#endif