Very experimental attempt to integrate EPG scanning into the idle scan.

This commit is contained in:
Adam Sutton 2012-06-21 13:50:09 +01:00
parent ae0cde8b50
commit 4cb753cc20
10 changed files with 315 additions and 115 deletions

View file

@ -76,8 +76,9 @@ SRCS = src/main.c \
SRCS += src/epggrab/pyepg.c\
src/epggrab/xmltv.c\
src/epggrab/ota.c \
src/epggrab/eit.c \
src/epggrab/opentv.c
src/epggrab/opentv.c \
SRCS += src/plumbing/tsfix.c \
src/plumbing/globalheaders.c \

View file

@ -120,6 +120,8 @@ typedef struct th_dvb_mux_instance {
int tdmi_enabled;
LIST_HEAD(, epggrab_ota_mux) tdmi_epg_grabbers;
time_t tdmi_got_adapter;
time_t tdmi_lost_adapter;
@ -148,13 +150,18 @@ typedef struct th_dvb_mux_instance {
*/
#define TDA_MUX_HASH_WIDTH 101
#define TDA_SCANQ_BAD 0 ///< Bad muxes (monitor quality)
#define TDA_SCANQ_OK 1 ///< OK muxes
#define TDA_SCANQ_EPG 2 ///< EPG muxes (TBD)
#define TDA_SCANQ_NUM 3
typedef struct th_dvb_adapter {
TAILQ_ENTRY(th_dvb_adapter) tda_global_link;
struct th_dvb_mux_instance_list tda_muxes;
struct th_dvb_mux_instance_queue tda_scan_queues[2];
struct th_dvb_mux_instance_queue tda_scan_queues[TDA_SCANQ_NUM];
int tda_scan_selector;
struct th_dvb_mux_instance_queue tda_initial_scan_queue;

View file

@ -41,6 +41,7 @@
#include "tsdemux.h"
#include "notify.h"
#include "service.h"
#include "epggrab/ota.h"
struct th_dvb_adapter_queue dvb_adapters;
struct th_dvb_mux_instance_tree dvb_muxes;
@ -53,11 +54,12 @@ static void *dvb_adapter_input_dvr(void *aux);
static th_dvb_adapter_t *
tda_alloc(void)
{
int i;
th_dvb_adapter_t *tda = calloc(1, sizeof(th_dvb_adapter_t));
pthread_mutex_init(&tda->tda_delivery_mutex, NULL);
TAILQ_INIT(&tda->tda_scan_queues[0]);
TAILQ_INIT(&tda->tda_scan_queues[1]);
for (i = 0; i < TDA_SCANQ_NUM; i++ )
TAILQ_INIT(&tda->tda_scan_queues[i]);
TAILQ_INIT(&tda->tda_initial_scan_queue);
TAILQ_INIT(&tda->tda_satconfs);
@ -418,13 +420,21 @@ dvb_adapter_init(uint32_t adapter_mask)
void
dvb_adapter_mux_scanner(void *aux)
{
epggrab_ota_mux_t *ota;
th_dvb_adapter_t *tda = aux;
th_dvb_mux_instance_t *tdmi;
int i;
int idle_epg;
static const char* scan_string[] = {
"Autoscan BAD",
"Autoscan OK",
"Autoscan EPG"
};
if(tda->tda_rootpath == NULL)
return; // No hardware
// default period
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 20);
if(LIST_FIRST(&tda->tda_muxes) == NULL)
@ -439,8 +449,18 @@ dvb_adapter_mux_scanner(void *aux)
return;
}
if(!tda->tda_idlescan && TAILQ_FIRST(&tda->tda_scan_queues[0]) == NULL) {
/* Idlescan is disabled and no muxes are bad */
/* Mark any incomplete EPG grabbers as timed out (basically complete) */
if (tda->tda_mux_current)
LIST_FOREACH(ota, &tda->tda_mux_current->tdmi_epg_grabbers, tdmi_link) {
epggrab_ota_timeout(ota);
}
/* Idle or EPG scan enabled */
idle_epg = tda->tda_idlescan ||
TAILQ_FIRST(&tda->tda_scan_queues[TDA_SCANQ_EPG]);
/* Idlescan is disabled and no muxes are bad */
if(!idle_epg && TAILQ_FIRST(&tda->tda_scan_queues[TDA_SCANQ_BAD]) == NULL) {
if(!tda->tda_qmon)
return; // Quality monitoring is disabled
@ -452,12 +472,26 @@ dvb_adapter_mux_scanner(void *aux)
return;
}
/* Alternate between the other two (bad and OK) */
for(i = 0; i < 2; i++) {
tda->tda_scan_selector = !tda->tda_scan_selector;
/* Alternate between the three queues (BAD, OK, EPG) */
for(i = 0; i < TDA_SCANQ_NUM; i++) {
tda->tda_scan_selector++;
if (tda->tda_scan_selector == TDA_SCANQ_NUM)
tda->tda_scan_selector = 0;
tdmi = TAILQ_FIRST(&tda->tda_scan_queues[tda->tda_scan_selector]);
if(tdmi != NULL) {
dvb_fe_tune(tdmi, "Autoscan");
/* EPG - adjust dwell time */
if (tda->tda_scan_selector == TDA_SCANQ_EPG) {
epggrab_ota_mux_t *ota;
int period = 20;
LIST_FOREACH(ota, &tdmi->tdmi_epg_grabbers, tdmi_link) {
if (ota->timeout > period) period = ota->timeout;
}
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, period);
}
dvb_fe_tune(tdmi, scan_string[tda->tda_scan_selector]);
return;
}
}

View file

@ -228,7 +228,17 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi)
assert(tdmi->tdmi_scan_queue == NULL);
if(tdmi->tdmi_enabled) {
tdmi->tdmi_scan_queue = &tda->tda_scan_queues[tdmi->tdmi_quality == 100];
int ti;
time_t now;
epggrab_ota_mux_t *ota;
time(&now);
LIST_FOREACH(ota, &tdmi->tdmi_epg_grabbers, tdmi_link) {
if ( now >= (ota->completed+ota->interval) ) break;
}
ti = ota ? TDA_SCANQ_EPG
: tdmi->tdmi_quality == 100 ? TDA_SCANQ_OK
: TDA_SCANQ_BAD;
tdmi->tdmi_scan_queue = &tda->tda_scan_queues[ti];
TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
}

View file

@ -44,6 +44,7 @@
#include "dvb_support.h"
#include "notify.h"
#include "subscriptions.h"
#include "epggrab/ota.h"
struct th_dvb_mux_instance_tree dvb_muxes;
@ -297,6 +298,7 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc,
void
dvb_mux_destroy(th_dvb_mux_instance_t *tdmi)
{
epggrab_ota_mux_t *ota;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
service_t *t;
@ -330,6 +332,9 @@ dvb_mux_destroy(th_dvb_mux_instance_t *tdmi)
if(tdmi->tdmi_table_initial)
tda->tda_initial_num_mux--;
while ((ota = LIST_FIRST(&tdmi->tdmi_epg_grabbers)))
epggrab_ota_unregister(ota);
hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier);
free(tdmi->tdmi_network);

View file

@ -10,7 +10,7 @@
#include "htsmsg.h"
#include "settings.h"
#include "tvheadend.h"
#include "src/queue.h"
#include "queue.h"
#include "epg.h"
#include "epggrab.h"
#include "epggrab/eit.h"
@ -834,6 +834,18 @@ void epggrab_channel_mod ( channel_t *ch )
void epggrab_tune ( th_dvb_mux_instance_t *tdmi )
{
epggrab_module_t *m;
epggrab_ota_mux_t *o;
th_dvb_mux_instance_t *t;
/* Cancel all currently active ota grabs */
LIST_FOREACH(t, &tdmi->tdmi_adapter->tda_muxes, tdmi_adapter_link) {
if (t == tdmi) continue;
LIST_FOREACH(o, &t->tdmi_epg_grabbers, tdmi_link) {
epggrab_ota_cancel(o);
}
}
/* Inform all modules */
LIST_FOREACH(m, &epggrab_modules, link) {
if (m->tune) m->tune(m, tdmi);
}

View file

@ -1,11 +1,12 @@
#ifndef __EPGGRAB_H__
#define __EPGGRAB_H__
typedef struct epggrab_module epggrab_module_t;
#include <pthread.h>
#include "channels.h"
#include "dvb/dvb.h"
typedef struct epggrab_module epggrab_module_t;
#include "epggrab/ota.h"
/* **************************************************************************
* Grabber Stats
@ -96,13 +97,14 @@ struct epggrab_module
{
LIST_ENTRY(epggrab_module) link; ///< Global list link
const char *id; ///< Module identifier
const char *name; ///< Module name (for display)
const char *path; ///< Module path (for fixed config)
const uint8_t flags; ///< Mode flag
uint8_t enabled; ///< Whether the module is enabled
int sock; ///< Socket descriptor
epggrab_channel_tree_t *channels; ///< Channel list
const char *id; ///< Module identifier
const char *name; ///< Module name (for display)
const char *path; ///< Module path (for fixed config)
const uint8_t flags; ///< Mode flag
uint8_t enabled; ///< Whether the module is enabled
int sock; ///< Socket descriptor
epggrab_channel_tree_t *channels; ///< Channel list
LIST_HEAD(, epggrab_ota_mux) muxes; ///< Linked muxes
/* Enable/Disable */
int (*enable) ( epggrab_module_t *m, uint8_t e );

View file

@ -25,6 +25,7 @@
#include "channels.h"
#include "huffman.h"
#include "epg.h"
#include "epggrab/ota.h"
#include "epggrab/opentv.h"
#include "subscriptions.h"
#include "streaming.h"
@ -42,6 +43,9 @@
* dicts/provs etc... they're only used to get things up and running
* ***********************************************************************/
#define OPENTV_SCAN_MAX 600 // 10min max scan period
#define OPENTV_SCAN_PER 3600 // 1hour interval
/* Huffman dictionary */
typedef struct opentv_dict
{
@ -378,10 +382,23 @@ static int _opentv_parse_event_section
return 0;
}
static void _opentv_table_callback
( opentv_module_t *m, th_dvb_mux_instance_t *tdmi )
{
epggrab_ota_mux_t *ota;
ota = epggrab_ota_register((epggrab_module_t*)m, tdmi,
OPENTV_SCAN_MAX, OPENTV_SCAN_PER);
if (epggrab_ota_begin(ota)) {
// setup internal status
}
}
static int _opentv_event_callback
( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p )
{
return _opentv_parse_event_section((opentv_module_t*)p, buf, len);
opentv_module_t *mod = (opentv_module_t*)p;
_opentv_table_callback(mod, tdmi);
return _opentv_parse_event_section(mod, buf, len);
}
// TODO: this function is currently a bit of a mess
@ -395,7 +412,7 @@ static int _opentv_channel_callback
uint16_t sid;
int i, j, k, tdlen, dlen, dtag, tslen;
channel_t *ch;
if (tid != 0x4a) return 0;
_opentv_table_callback(mod, tdmi);
i = 7 + ((((int)buf[5] & 0xf) << 8) | buf[6]);
tslen = (((int)buf[i] & 0xf) << 8) | buf[i+1];
i += 2;
@ -441,82 +458,6 @@ static int _opentv_channel_callback
return 0;
}
/* ************************************************************************
* Tuning Thread
* ***********************************************************************/
static void _opentv_stream ( void *p, streaming_message_t *sm )
{
// TODO: handle start?
// TODO: handle stop?
}
// TODO: if channel not found we still wait
// TODO: make time periods configurable?
// TODO: dynamic detection of when to start/stop
static void* _opentv_thread ( void *p )
{
int err;
service_t *svc;
th_subscription_t *sub;
streaming_target_t stream;
time_t start;
struct timespec ts;
opentv_module_t *mod = (opentv_module_t*)p;
streaming_target_init(&stream, _opentv_stream, NULL, 0);
tvhlog(LOG_INFO, mod->id, "thread started\n");
do {
/* Find channel and subscribe */
tvhlog(LOG_DEBUG, mod->id, "grab begin %d %d", mod->prov->tsid, mod->prov->sid);
pthread_mutex_lock(&global_lock);
svc = _opentv_find_service(mod->prov->tsid, mod->prov->sid);
if (svc) {
sub = subscription_create_from_service(svc, mod->prov->id,
&stream, 0);
if (sub) subscription_change_weight(sub, 1);
}
else
sub = NULL;
pthread_mutex_unlock(&global_lock);
if (sub) tvhlog(LOG_DEBUG, mod->id, "subscription added");
/* Allow scanning */
if (sub) {
time(&start);
ts.tv_nsec = 0;
pthread_mutex_lock(&mod->mutex);
while ( mod->enabled ) {
ts.tv_sec = start + 300;
err = pthread_cond_timedwait(&mod->cond, &mod->mutex, &ts);
if (err == ETIMEDOUT ) break;
}
pthread_mutex_unlock(&mod->mutex);
}
tvhlog(LOG_DEBUG, mod->id, "grab complete");
/* Terminate subscription */
pthread_mutex_lock(&global_lock);
if (sub) subscription_unsubscribe(sub);
pthread_mutex_unlock(&global_lock);
/* Wait */
time(&mod->updated);
pthread_mutex_lock(&mod->mutex);
while ( mod->enabled ) {
ts.tv_sec = mod->updated + 3600;
err = pthread_cond_timedwait(&mod->cond, &mod->mutex, &ts);
if (err == ETIMEDOUT) break;
}
if (!mod->enabled) break;
pthread_mutex_unlock(&mod->mutex);
} while (1);
pthread_mutex_unlock(&mod->mutex);
return NULL;
}
/* ************************************************************************
* Module Setup
* ***********************************************************************/
@ -528,7 +469,7 @@ static void _opentv_tune ( epggrab_module_t *m, th_dvb_mux_instance_t *tdmi )
int *t;
struct dmx_sct_filter_params *fp;
opentv_module_t *mod = (opentv_module_t*)m;
/* Install tables */
if (m->enabled && (mod->prov->tsid == tdmi->tdmi_transport_stream_id)) {
tvhlog(LOG_INFO, "opentv", "install provider %s tables", mod->prov->id);
@ -568,23 +509,27 @@ static void _opentv_tune ( epggrab_module_t *m, th_dvb_mux_instance_t *tdmi )
static int _opentv_enable ( epggrab_module_t *m, uint8_t e )
{
int save = 0;
pthread_t t;
pthread_attr_t ta;
th_dvb_adapter_t *tda;
th_dvb_mux_instance_t *tdmi;
opentv_module_t *mod = (opentv_module_t*)m;
pthread_mutex_lock(&mod->mutex);
if (m->enabled != e) {
m->enabled = e;
if (e) {
pthread_attr_init(&ta);
pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED);
pthread_create(&t, &ta, _opentv_thread, mod);
} else
pthread_cond_signal(&mod->cond);
save = 1;
if (m->enabled == e) return 0;
m->enabled = e;
/* Find muxes and enable/disable */
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
if (tdmi->tdmi_transport_stream_id != mod->prov->tsid) continue;
if (e) {
epggrab_ota_register(m, tdmi, OPENTV_SCAN_MAX, OPENTV_SCAN_PER);
} else {
epggrab_ota_unregister_one(m, tdmi);
}
}
}
pthread_mutex_unlock(&mod->mutex);
return save;
return 1;
}
void opentv_init ( epggrab_module_list_t *list )

110
src/epggrab/ota.c Normal file
View file

@ -0,0 +1,110 @@
/*
* Electronic Program Guide - EPG grabber OTA functions
* Copyright (C) 2012 Adam Sutton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tvheadend.h"
#include "queue.h"
#include "epg.h"
#include "dvb/dvb.h"
#include "epggrab.h"
#include "epggrab/ota.h"
LIST_HEAD(,epggrab_ota_mux) ota_muxes;
epggrab_ota_mux_t *epggrab_ota_register
( epggrab_module_t *mod, th_dvb_mux_instance_t *tdmi,
int timeout, int interval )
{
epggrab_ota_mux_t *ota;
/* Check for existing */
LIST_FOREACH(ota, &ota_muxes, glob_link) {
if (ota->grab == mod && ota->tdmi == tdmi) return ota;
}
/* Install new */
ota = calloc(1, sizeof(epggrab_ota_mux_t));
ota->grab = mod;
ota->tdmi = tdmi;
ota->timeout = timeout;
ota->interval = interval;
LIST_INSERT_HEAD(&ota_muxes, ota, glob_link);
LIST_INSERT_HEAD(&tdmi->tdmi_epg_grabbers, ota, tdmi_link);
LIST_INSERT_HEAD(&mod->muxes, ota, grab_link);
return ota;
}
/*
* Unregister
*/
void epggrab_ota_unregister ( epggrab_ota_mux_t *ota )
{
LIST_REMOVE(ota, glob_link);
LIST_REMOVE(ota, tdmi_link);
LIST_REMOVE(ota, grab_link);
free(ota);
}
void epggrab_ota_unregister_one
( epggrab_module_t *mod, th_dvb_mux_instance_t *tdmi )
{
epggrab_ota_mux_t *ota;
LIST_FOREACH(ota, &ota_muxes, glob_link) {
if (ota->grab == mod && (!tdmi || ota->tdmi == tdmi)) {
epggrab_ota_unregister(ota);
if (tdmi) break;
}
}
}
void epggrab_ota_unregister_all
( epggrab_module_t *mod )
{
epggrab_ota_unregister_one(mod, NULL);
}
/*
* Status
*/
int epggrab_ota_begin ( epggrab_ota_mux_t *ota )
{
if (ota->status != EPGGRAB_OTA_STARTED) {
ota->status = EPGGRAB_OTA_STARTED;
time(&ota->started);
return 1;
}
return 0;
}
void epggrab_ota_complete ( epggrab_ota_mux_t *ota )
{
if (ota->status != EPGGRAB_OTA_COMPLETED)
time(&ota->completed);
ota->status = EPGGRAB_OTA_COMPLETED;
}
void epggrab_ota_cancel ( epggrab_ota_mux_t *ota )
{
ota->status = EPGGRAB_OTA_IDLE;
}
void epggrab_ota_timeout ( epggrab_ota_mux_t *ota )
{
epggrab_ota_complete(ota);
// TODO: for now just treat as completed, stops it being allowed
// to immediately re-enter the queue
}

74
src/epggrab/ota.h Normal file
View file

@ -0,0 +1,74 @@
/*
* Electronic Program Guide - EPG grabber OTA functions
* Copyright (C) 2012 Adam Sutton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TVH_EPGGRAB_OTA_H__
#define __TVH_EPGGRAB_OTA_H__
#include "queue.h"
#include <sys/time.h>
/*
* Mux to OTA Grabber link
*/
typedef struct epggrab_ota_mux
{
LIST_ENTRY(epggrab_ota_mux) glob_link; ///< Global list of all instances
LIST_ENTRY(epggrab_ota_mux) grab_link; ///< Grabber's list link
LIST_ENTRY(epggrab_ota_mux) tdmi_link; ///< Mux list link
struct epggrab_module *grab; ///< Grabber module
struct th_dvb_mux_instance *tdmi; ///< Mux instance
int timeout; ///< Time out if this long
int interval; ///< Re-grab this often
enum {
EPGGRAB_OTA_IDLE,
EPGGRAB_OTA_STARTED,
EPGGRAB_OTA_COMPLETED
} status; ///< Status of the scan
time_t started; ///< Time of last start
time_t completed; ///< Time of last completion
} epggrab_ota_mux_t;
/*
* Register interest (will return existing if already registered)
*/
epggrab_ota_mux_t *epggrab_ota_register
( struct epggrab_module *mod, struct th_dvb_mux_instance *tdmi,
int timeout, int interval );
/*
* Unregister
*/
void epggrab_ota_unregister
( epggrab_ota_mux_t *ota );
void epggrab_ota_unregister_one
( struct epggrab_module *mod, struct th_dvb_mux_instance *tdmi );
void epggrab_ota_unregister_all
( struct epggrab_module *mod );
/*
* Status
*/
int epggrab_ota_begin ( epggrab_ota_mux_t *ota );
void epggrab_ota_complete ( epggrab_ota_mux_t *ota );
void epggrab_ota_cancel ( epggrab_ota_mux_t *ota );
void epggrab_ota_timeout ( epggrab_ota_mux_t *ota );
#endif /* __TVH_EPGGRAB_OTA_H__ */