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