epggrab: complete update to OTA mux and EIT

Still haven't touched OpenTV and there are several hacks in here for which
a proper solution is required.
(cherry picked from commit 9212120eb4c92aacb0d9cb75624961bf27e2ef5a)
This commit is contained in:
Adam Sutton 2013-08-23 15:39:25 +01:00
parent c022ab7c75
commit df44bad3e9
18 changed files with 504 additions and 855 deletions

View file

@ -157,21 +157,23 @@ SRCS += src/muxer.c \
# MPEGTS core
SRCS-$(CONFIG_MPEGTS) += \
src/input/mpegts/mpegts_input.c \
src/input/mpegts/mpegts_network.c \
src/input/mpegts/mpegts_mux.c \
src/input/mpegts/mpegts_service.c \
src/input/mpegts/mpegts_table.c \
src/input/mpegts/mpegts_input.c \
src/input/mpegts/mpegts_network.c \
src/input/mpegts/mpegts_mux.c \
src/input/mpegts/mpegts_service.c \
src/input/mpegts/mpegts_table.c \
src/input/mpegts/dvb_support.c \
src/input/mpegts/dvb_charset.c \
src/input/mpegts/dvb_psi.c \
src/input/mpegts/tsdemux.c \
# MPEGTS EPG
#SRCS-$(CONFIG_MPEGTS) += \
# src/epggrab/otamux.c\
# src/epggrab/module/eit.c \
SRCS-$(CONFIG_MPEGTS) += \
src/epggrab/otamux.c\
src/epggrab/module/eit.c \
src/epggrab/support/freesat_huffman.c \
# src/epggrab/module/opentv.c \
# src/epggrab/support/freesat_huffman.c \
# DVB
SRCS-${CONFIG_LINUXDVB} += \

View file

@ -386,11 +386,14 @@ void epggrab_init ( void )
pthread_mutex_init(&epggrab_mutex, NULL);
pthread_cond_init(&epggrab_cond, NULL);
/* Initialise the OTA subsystem */
epggrab_ota_init();
/* Initialise modules */
#if 0//ENABLE_LINUXDVB
#if ENABLE_MPEGTS
eit_init();
opentv_init();
//opentv_init();
#endif
pyepg_init();
xmltv_init();

View file

@ -19,24 +19,26 @@
#ifndef __EPGGRAB_H__
#define __EPGGRAB_H__
#include "idnode.h"
#include <pthread.h>
/* **************************************************************************
* Typedefs/Forward decls
* *************************************************************************/
struct th_dvb_mux_instance;
struct dvb_network;
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;
typedef struct epggrab_ota_map epggrab_ota_map_t;
LIST_HEAD(epggrab_module_list, epggrab_module);
typedef struct epggrab_module_list epggrab_module_list_t;
struct mpegts_mux;
/* **************************************************************************
* Grabber Stats
* *************************************************************************/
@ -165,32 +167,35 @@ struct epggrab_module_ext
};
/*
* OTA / mux link
* TODO: this could be embedded in the mux itself, but by using a soft-link
* and keeping it here I can somewhat isolate it from the mpegts code
*/
struct epggrab_ota_mux
{
TAILQ_ENTRY(epggrab_ota_mux) glob_link;///< Grabber link
TAILQ_ENTRY(epggrab_ota_mux) dm_link; ///< Link to dvb mux
TAILQ_ENTRY(epggrab_ota_mux) grab_link;///< Link to grabber
struct dvb_mux *dm; ///< Mux instance
epggrab_module_ota_t *grab; ///< Grab instance
idnode_t om_id;
char *om_mux_uuid; ///< Soft-link to mux
LIST_HEAD(,epggrab_ota_map) om_modules; ///< List of linked mods
int om_active;
int om_timeout; ///< User configurable
int om_interval;
time_t om_when; ///< Next event time
LIST_ENTRY(epggrab_ota_mux) om_q_link;
RB_ENTRY(epggrab_ota_mux) om_global_link;
};
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
/*
* Link between ota_mux and ota_module
*/
struct epggrab_ota_map
{
LIST_ENTRY(epggrab_ota_map) om_link;
epggrab_module_ota_t *om_module;
int om_timeout;
int om_interval;
int om_complete;
};
/*
@ -200,10 +205,10 @@ struct epggrab_module_ota
{
epggrab_module_t ; ///< Parent object
TAILQ_HEAD(, epggrab_ota_mux) muxes; ///< List of related muxes
//TAILQ_HEAD(, epggrab_ota_mux) muxes; ///< List of related muxes
/* Transponder tuning */
void (*start) ( epggrab_module_ota_t *m, struct dvb_mux *dm );
void (*start) ( epggrab_module_ota_t *m, struct mpegts_mux *mm );
};
/*
@ -246,6 +251,7 @@ int epggrab_enable_module_by_id ( const char *id, uint8_t e );
*/
void epggrab_init ( void );
void epggrab_save ( void );
void epggrab_ota_init ( void );
/* **************************************************************************
* Global Functions
@ -258,15 +264,6 @@ void epggrab_channel_add ( struct channel *ch );
void epggrab_channel_rem ( struct channel *ch );
void epggrab_channel_mod ( struct channel *ch );
/*
* Transport handling
*/
void epggrab_mux_start ( struct dvb_mux *tdmi );
void epggrab_mux_stop ( struct dvb_mux *tdmi, int timeout );
void epggrab_mux_delete ( struct dvb_mux *tdmi );
int epggrab_mux_period ( struct dvb_mux *tdmi );
struct dvb_mux *epggrab_mux_next ( struct dvb_network *dn );
/*
* Re-schedule
*/

View file

@ -458,7 +458,7 @@ epggrab_module_ota_t *epggrab_module_ota_create
( epggrab_module_ota_t *skel,
const char *id, const char *name, int priority,
void (*start) (epggrab_module_ota_t*m,
struct dvb_mux *dm),
struct mpegts_mux *dm),
int (*enable) (void *m, uint8_t e ),
epggrab_channel_tree_t *channels )
{
@ -471,7 +471,7 @@ epggrab_module_ota_t *epggrab_module_ota_create
skel->type = EPGGRAB_OTA;
skel->enable = enable;
skel->start = start;
TAILQ_INIT(&skel->muxes);
//TAILQ_INIT(&skel->muxes);
return skel;
}

View file

@ -20,131 +20,18 @@
#include "tvheadend.h"
#include "channels.h"
#include "dvb/dvb.h"
#include "dvb/dvb_support.h"
#include "service.h"
#include "epg.h"
#include "epggrab.h"
#include "epggrab/private.h"
#include "dvb/dvb_charset.h"
#include "input/mpegts.h"
#include "input/mpegts/dvb.h"
#include "input/mpegts/dvb_charset.h"
/* ************************************************************************
* Status handling
* ***********************************************************************/
typedef struct eit_table_status
{
LIST_ENTRY(eit_table_status) link;
int tid;
uint16_t onid;
uint16_t tsid;
uint16_t sid;
uint32_t sec[8];
uint8_t ver;
enum {
EIT_STATUS_START,
EIT_STATUS_PROCESS,
EIT_STATUS_LAST,
EIT_STATUS_DONE
} state;
} eit_table_status_t;
typedef struct eit_status
{
eit_table_status_t *first;
LIST_HEAD(, eit_table_status) tables;
} eit_status_t;
static eit_table_status_t *eit_status_find
( eit_status_t *status, int tableid,
uint16_t onid, uint16_t tsid, uint16_t sid,
uint8_t sec, uint8_t lst, uint8_t seg, uint8_t ver )
{
int i, sec_index;
uint32_t sec_seen_mask;
eit_table_status_t *sta;
/* Find */
LIST_FOREACH(sta, &status->tables, link)
if (sta->tid == tableid && sta->tsid == tsid && sta->onid == onid &&
sta->sid == sid)
break;
/* Already complete */
if (sta && sta->state == EIT_STATUS_DONE && sta->ver == ver) return sta;
/* Insert new entry */
if (!sta) {
sta = calloc(1, sizeof(eit_table_status_t));
LIST_INSERT_HEAD(&status->tables, sta, link);
sta->tid = tableid;
sta->onid = onid;
sta->tsid = tsid;
sta->sid = sid;
sta->ver = 255; // Note: force update below
}
/* Reset */
if (sta->ver != ver) {
sta->ver = ver;
for (i = 0; i < (lst / 32); i++)
sta->sec[i] = 0xFFFFFFFF;
sta->sec[i] = (0xFFFFFFFF >> (31-(lst%32)));
sta->state = EIT_STATUS_PROCESS;
}
/* Get "section(s) seen" mask. See ETSI TS 101 211 (V1.11.1) section 4.1.4
* and ETSI EN 300 468 (V1.13.1) section 5.2.4 for usage of the
* of the "seg" (= segment_last_section_number) field in the
* now/next (tableid < 0x50) and schedule (tableid >= 0x50) tables
*/
sec_index = sec/32;
sec_seen_mask = 0x1 << (sec%32);
if (tableid >= 0x50) {
// ETSI TS 101 211 (V1.11.1) section 4.1.4 specifies seg in eit schedule tables
// as the index of the first section in the segment (always a multiple of 8,
// because a segment has eight sections) plus an offset thaqt depends on the
// number of sections used:
// * seg_first_section + n - 1 if 0<n<8 segments are used (see section 4.1.4 e)
// * seg_first_section + 7 if all segments are used (see section 4.1.4 f)
// * seg_first_section + 0 if the section is empty (see section 4.1.4 g)
// This means that we can calculate the mask offset of the last used section
// simply by taking the lowest three bits of seg + 1 (empty segments count as
// "one section used"). We use this offset to calculate a mask for all *unused*
// sections by shifting 0xff left by this offset, take the lowest eight bits, shift
// them left by the first section offset and finally expand seen_mask (seen_mask)
// by calculating "seen_mask |= unused_mask"
uint32_t seg_first_section = seg & ~0x07;
uint32_t seg_last_used_section_offset = (seg & 0x07) + 1;
uint32_t unused_mask = ((0xff << seg_last_used_section_offset) & 0xff) << (seg_first_section%32);
sec_seen_mask |= unused_mask;
}
/* Already seen */
if (!(sta->sec[sec_index] & sec_seen_mask)) {
if (sta->state == EIT_STATUS_START) {
sta->state = EIT_STATUS_DONE;
goto done;
}
return NULL;
}
/* Update */
sta->sec[sec_index] &= ~sec_seen_mask;
/* Check complete? */
done:
sta->state = EIT_STATUS_LAST;
for (i = 0; i < 8; i++ ) {
if (sta->sec[i]) {
sta->state = EIT_STATUS_PROCESS;
break;
}
}
return sta;
}
typedef struct eit_event
{
char uri[257];
@ -173,7 +60,9 @@ typedef struct eit_event
* ***********************************************************************/
// Dump a descriptor tag for debug (looking for new tags etc...)
static void _eit_dtag_dump ( epggrab_module_t *mod, uint8_t dtag, uint8_t dlen, uint8_t *buf )
static void
_eit_dtag_dump
( epggrab_module_t *mod, uint8_t dtag, uint8_t dlen, const uint8_t *buf )
{
#if APS_DEBUG
int i = 0, j = 0;
@ -224,7 +113,7 @@ static int _eit_get_string_with_len
* Short Event - 0x4d
*/
static int _eit_desc_short_event
( epggrab_module_t *mod, uint8_t *ptr, int len, eit_event_t *ev )
( epggrab_module_t *mod, const uint8_t *ptr, int len, eit_event_t *ev )
{
int r;
char lang[4];
@ -267,12 +156,12 @@ static int _eit_desc_short_event
* Extended Event - 0x4e
*/
static int _eit_desc_ext_event
( epggrab_module_t *mod, uint8_t *ptr, int len, eit_event_t *ev )
( epggrab_module_t *mod, const uint8_t *ptr, int len, eit_event_t *ev )
{
int r, ilen;
char ikey[512], ival[512];
char buf[512], lang[4];
uint8_t *iptr;
const uint8_t *iptr;
if (len < 6) return -1;
@ -343,7 +232,7 @@ static int _eit_desc_ext_event
*/
static int _eit_desc_component
( epggrab_module_t *mod, uint8_t *ptr, int len, eit_event_t *ev )
( epggrab_module_t *mod, const uint8_t *ptr, int len, eit_event_t *ev )
{
uint8_t c, t;
@ -401,7 +290,7 @@ static int _eit_desc_component
*/
static int _eit_desc_content
( epggrab_module_t *mod, uint8_t *ptr, int len, eit_event_t *ev )
( epggrab_module_t *mod, const uint8_t *ptr, int len, eit_event_t *ev )
{
while (len > 1) {
if (*ptr == 0xb1)
@ -420,7 +309,7 @@ static int _eit_desc_content
* Parental rating Descriptor - 0x55
*/
static int _eit_desc_parental
( epggrab_module_t *mod, uint8_t *ptr, int len, eit_event_t *ev )
( epggrab_module_t *mod, const uint8_t *ptr, int len, eit_event_t *ev )
{
int cnt = 0, sum = 0, i = 0;
while (len > 3) {
@ -442,7 +331,8 @@ static int _eit_desc_parental
* Content ID - 0x76
*/
static int _eit_desc_crid
( epggrab_module_t *mod, uint8_t *ptr, int len, eit_event_t *ev, service_t *svc )
( epggrab_module_t *mod, const uint8_t *ptr, int len,
eit_event_t *ev, mpegts_service_t *svc )
{
int r;
uint8_t type;
@ -479,9 +369,9 @@ static int _eit_desc_crid
} else if ( *buf != '/' ) {
snprintf(crid, clen, "crid://%s", buf);
} else {
char *defauth = svc->s_default_authority;
char *defauth = svc->s_dvb_cridauth;
if (!defauth)
defauth = svc->s_dvb_mux->dm_default_authority;
defauth = svc->s_dvb_mux->mm_crid_authority;
if (defauth)
snprintf(crid, clen, "crid://%s%s", defauth, buf);
}
@ -503,7 +393,7 @@ static int _eit_desc_crid
static int _eit_process_event
( epggrab_module_t *mod, int tableid,
service_t *svc, uint8_t *ptr, int len,
mpegts_service_t *svc, const uint8_t *ptr, int len,
int *resched, int *save )
{
int save2 = 0;
@ -515,6 +405,7 @@ static int _eit_process_event
epg_episode_t *ee;
epg_serieslink_t *es;
eit_event_t ev;
channel_t *ch = LIST_FIRST(&svc->s_channels)->csm_chn;
if ( len < 12 ) return -1;
@ -532,9 +423,9 @@ static int _eit_process_event
ret = 12 + dllen;
/* Find broadcast */
ebc = epg_broadcast_find_by_time(svc->s_ch, start, stop, eid, 1, &save2);
ebc = epg_broadcast_find_by_time(NULL, start, stop, eid, 1, &save2);
tvhtrace("eit", "eid=%5d, start=%"PRItime_t", stop=%"PRItime_t", ebc=%p",
eid, start, stop, ebc);
eid, start, stop, ebc);
if (!ebc) return dllen + 12;
/* Mark re-schedule detect (only now/next) */
@ -548,8 +439,8 @@ static int _eit_process_event
/* Override */
if (!ev.default_charset) {
ev.default_charset
= dvb_charset_find(svc->s_dvb_mux->dm_network_id,
svc->s_dvb_mux->dm_transport_stream_id,
= dvb_charset_find(svc->s_dvb_mux->mm_onid,
svc->s_dvb_mux->mm_tsid,
svc->s_dvb_service_id);
}
@ -557,6 +448,8 @@ static int _eit_process_event
int r;
dtag = ptr[0];
dlen = ptr[1];
tvhtrace(mod->id, " dtag %02X dlen %d", dtag, dlen);
tvhlog_hexdump(mod->id, ptr+2, dlen);
dllen -= 2;
ptr += 2;
@ -659,88 +552,74 @@ static int _eit_process_event
}
static int
_eit_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
_eit_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
epggrab_module_t *mod = opaque;
epggrab_ota_mux_t *ota;
service_t *svc;
eit_status_t *sta;
eit_table_status_t *tsta;
int resched = 0, save = 0;
int r;
int sect, last, ver, save, resched;
uint8_t seg;
uint16_t onid, tsid, sid;
uint16_t sec, lst, seg, ver;
uint32_t extraid;
mpegts_service_t *svc;
mpegts_mux_t *mm = mt->mt_mux;;
epggrab_module_t *mod = mt->mt_opaque;
epggrab_ota_mux_t *ota = NULL;
mpegts_table_state_t *st;
/* Invalid */
if(tableid < 0x4e || tableid > 0x6f || len < 11) return -1;
/* Validate */
if(tableid < 0x4e || tableid > 0x6f || len < 11)
return -1;
/* Get OTA */
ota = epggrab_ota_find((epggrab_module_ota_t*)mod, dm);
if (!ota || !ota->status) return -1;
sta = ota->status;
/* Basic info */
sid = ptr[0] << 8 | ptr[1];
tsid = ptr[5] << 8 | ptr[6];
onid = ptr[7] << 8 | ptr[8];
seg = ptr[9];
extraid = ((uint32_t)tsid << 16) | sid;
// TODO: extra ID should probably include onid
/* Begin: reset sta->state to force revisiting of all tables */
if (epggrab_ota_begin(ota)) {
sta->first = NULL;
LIST_FOREACH(tsta, &sta->tables, link) {
tsta->state = EIT_STATUS_START;
}
/* Register interest */
if (tableid >= 0x50)
ota = epggrab_ota_register((epggrab_module_ota_t*)mod, mm, 1200, 3600);
/* Begin */
r = dvb_table_begin(mt, ptr, len, tableid, extraid, 11, &st, &sect, &last, &ver);
if (r != 1) return r;
if (st) {
uint32_t mask;
int sa = seg & 0xF8;
int sb = 7 - (seg & 0x07);
mask = (~(0xFF << sb) & 0xFF);
mask <<= (24 - (sa % 32));
st->sections[sa/32] &= ~mask;
}
/* Get table info */
sid = ptr[0] << 8 | ptr[1];
tsid = ptr[5] << 8 | ptr[6];
onid = ptr[7] << 8 | ptr[8];
sec = ptr[3];
lst = ptr[4];
seg = ptr[9];
ver = (ptr[2] >> 1) & 0x1f;
tvhtrace("eit",
"tid=0x%02X, onid=0x%04X, tsid=0x%04X, sid=0x%04X"
", sec=%3d/%3d, seg=%3d, ver=%2d, cur=%d",
tableid, onid, tsid, sid, sec, lst, seg, ver, ptr[2] & 1);
/* Don't process */
if((ptr[2] & 1) == 0) return 0;
/* Current status */
tsta = eit_status_find(sta, tableid, onid, tsid, sid, sec, lst, seg, ver);
tvhtrace("eit", tsta && tsta->state != EIT_STATUS_DONE ? "section process" : "section seen");
if (!tsta) return 0; // already seen, no state change
if (tsta->state == EIT_STATUS_DONE) goto done;
/* Get transport stream */
// Note: tableid=0x4f,0x60-0x6f is other TS
// so must find the tdmi
if(tableid == 0x4f || tableid >= 0x60) {
dm = dvb_mux_find(dm->dm_dn, NULL, onid, tsid, 1);
mm = mpegts_network_find_mux(mm->mm_network, onid, tsid);
} else {
if (dm->dm_transport_stream_id != tsid ||
dm->dm_network_id != onid) {
tvhtrace("eit",
"invalid tsid found tid 0x%02X, onid:tsid %d:%d != %d:%d",
tableid, dm->dm_network_id, dm->dm_transport_stream_id,
onid, tsid);
dm = NULL;
if (mm->mm_tsid != tsid ||
mm->mm_onid != onid) {
tvhwarn("eit",
"invalid tsid found tid 0x%02X, onid:tsid %d:%d != %d:%d",
tableid, mm->mm_onid, mm->mm_tsid, onid, tsid);
//dm = NULL;
}
}
if(!dm) goto done;
if(!mm)
goto done;
/* Get service */
svc = dvb_service_find3(NULL, dm, NULL, 0, 0, sid, 1, 1);
if (!svc || !svc->s_ch) goto done;
/* Register as interesting */
if (tableid < 0x50)
epggrab_ota_register(ota, 20, 300); // 20s grab, 5min interval
else
epggrab_ota_register(ota, 600, 3600); // 10min grab, 1hour interval
// Note: this does mean you will get a slight oddity for muxes that
// carry both, since they will end up with setting of 600/300
// Note: could we be more dynamic for now/next interval?
svc = mpegts_mux_find_service(mm, sid);
// TODO: have lost the concept of the primary EPG service!
if (!svc || !LIST_FIRST(&svc->s_channels))
goto done;
/* Process events */
save = resched = 0;
len -= 11;
ptr += 11;
while (len) {
@ -752,71 +631,27 @@ _eit_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
ptr += r;
}
/* Complete */
done:
if (tsta->state == EIT_STATUS_LAST)
tsta->state = EIT_STATUS_DONE;
if (tsta->state == EIT_STATUS_DONE) {
if (!sta->first)
sta->first = tsta;
else if (sta->first == tsta) {
LIST_FOREACH(tsta, &sta->tables, link)
if (tsta->state != EIT_STATUS_DONE) break;
if (!tsta) epggrab_ota_complete(ota);
}
}
#if ENABLE_TRACE
if (ota->state != EPGGRAB_OTA_MUX_COMPLETE)
{
int total = 0;
int finished = 0;
tvhtrace("eit", "scan status:");
LIST_FOREACH(tsta, &sta->tables, link) {
total++;
tvhtrace("eit",
" tid=0x%02X, onid=0x%04X, tsid=0x%04X, sid=0x%04X, ver=%02d"
", done=%d, "
"mask=%08X|%08X|%08X|%08X|%08X|%08X|%08X|%08X",
tsta->tid, tsta->onid, tsta->tsid, tsta->sid, tsta->ver,
tsta->state == EIT_STATUS_DONE,
tsta->sec[7], tsta->sec[6], tsta->sec[5], tsta->sec[4],
tsta->sec[3], tsta->sec[2], tsta->sec[1], tsta->sec[0]);
if (tsta->state == EIT_STATUS_DONE) finished++;
}
tvhtrace("eit", " completed %d of %d", finished, total);
}
#endif
/* Update EPG */
if (resched) epggrab_resched();
if (save) epg_updated();
return 0;
done:
r = dvb_table_end(mt, st, sect);
if (ota && !r)
epggrab_ota_complete((epggrab_module_ota_t*)mod, ota);
return r;
}
/* ************************************************************************
* Module Setup
* ***********************************************************************/
static void _eit_ota_destroy ( epggrab_ota_mux_t *ota )
{
eit_status_t *sta = ota->status;
eit_table_status_t *tsta;
/* Remove all entries */
while ((tsta = LIST_FIRST(&sta->tables))) {
LIST_REMOVE(tsta, link);
free(tsta);
}
free(sta);
free(ota);
}
static void _eit_start
( epggrab_module_ota_t *m, dvb_mux_t *dm )
( epggrab_module_ota_t *m, mpegts_mux_t *dm )
{
epggrab_ota_mux_t *ota;
int pid;
/* Disabled */
if (!m->enabled) return;
@ -827,74 +662,31 @@ static void _eit_start
if (m->enabled) return;
}
/* Register */
if (!(ota = epggrab_ota_create(m, dm))) return;
if (!ota->status) {
ota->status = calloc(1, sizeof(eit_status_t));
ota->destroy = _eit_ota_destroy;
}
/* Freesat (3002/3003) */
if (!strcmp("uk_freesat", m->id)) {
#ifdef IGNORE_TOO_SLOW
tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3840, NULL);
tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3841, NULL);
#endif
tdt_add(dm, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3002);
tdt_add(dm, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3003);
mpegts_table_add(dm, 0, 0, dvb_bat_callback, NULL, "bat", MT_CRC, 3002);
pid = 3003;
/* Viasat Baltic (0x39) */
} else if (!strcmp("viasat_baltic", m->id)) {
tdt_add(dm, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x39);
pid = 0x39;
/* Standard (0x12) */
} else {
tdt_add(dm, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x12);
pid = 0x12;
}
tvhlog(LOG_DEBUG, m->id, "install table handlers");
}
static int _eit_enable ( void *m, uint8_t e )
{
epggrab_module_ota_t *mod = m;
if (mod->enabled == e) return 0;
mod->enabled = e;
/* Register interest */
if (e) {
/* Freesat (register fixed MUX) */
if (!strcmp(mod->id, "uk_freesat")) {
#ifdef IGNORE_TOO_SLOW
epggrab_ota_create_and_register_by_id((epggrab_module_ota_t*)mod,
0, 2050,
600, 3600, "ASTRA");
#endif
epggrab_ota_create_and_register_by_id((epggrab_module_ota_t*)mod,
0, 2315,
600, 3600, "Freesat");
}
/* Remove all links */
} else {
epggrab_ota_destroy_by_module((epggrab_module_ota_t*)mod);
}
return 1;
mpegts_table_add(dm, 0, 0, _eit_callback, m, m->id, MT_CRC, pid);
tvhlog(LOG_DEBUG, m->id, "installed table handlers");
}
void eit_init ( void )
{
epggrab_module_ota_create(NULL, "eit", "EIT: DVB Grabber", 1,
_eit_start, _eit_enable, NULL);
_eit_start, NULL, NULL);
epggrab_module_ota_create(NULL, "uk_freesat", "UK: Freesat", 5,
_eit_start, _eit_enable, NULL);
_eit_start, NULL, NULL);
epggrab_module_ota_create(NULL, "uk_freeview", "UK: Freeview", 5,
_eit_start, _eit_enable, NULL);
_eit_start, NULL, NULL);
epggrab_module_ota_create(NULL, "viasat_baltic", "VIASAT: Baltic", 5,
_eit_start, _eit_enable, NULL);
}
void eit_load ( void )
{
_eit_start, NULL, NULL);
}

View file

@ -16,422 +16,301 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* TODO: currently I don't try to block multiple scans of the same
* tdmi on different adapters
*/
#include <string.h>
#include "tvheadend.h"
#include "queue.h"
#include "epg.h"
#include "dvb/dvb.h"
#include "epggrab.h"
#include "epggrab/private.h"
#include "input/mpegts.h"
TAILQ_HEAD(, epggrab_ota_mux) ota_mux_all;
RB_HEAD(,epggrab_ota_mux) epggrab_ota_all;
LIST_HEAD(,epggrab_ota_mux) epggrab_ota_pending;
LIST_HEAD(,epggrab_ota_mux) epggrab_ota_active;
gtimer_t epggrab_ota_pending_timer;
gtimer_t epggrab_ota_active_timer;
static void epggrab_ota_active_timer_cb ( void *p );
static void epggrab_ota_pending_timer_cb ( void *p );
/* **************************************************************************
* Global functions (called from DVB code)
* Utilities
* *************************************************************************/
void epggrab_mux_start ( dvb_mux_t *dm )
static int
om_time_cmp ( epggrab_ota_mux_t *a, epggrab_ota_mux_t *b )
{
return (a->om_when - b->om_when);
}
static int
om_id_cmp ( epggrab_ota_mux_t *a, epggrab_ota_mux_t *b )
{
return strcmp(a->om_mux_uuid, b->om_mux_uuid);
}
#define EPGGRAB_OTA_MIN_PERIOD 600
#define EPGGRAB_OTA_MIN_TIMEOUT 30
static int
epggrab_ota_period ( epggrab_ota_mux_t *ota )
{
int period = 0;
epggrab_ota_map_t *map;
if (ota->om_interval)
period = ota->om_interval;
else {
LIST_FOREACH(map, &ota->om_modules, om_link)
if (!period || map->om_interval < period)
period = map->om_interval;
}
if (period < EPGGRAB_OTA_MIN_PERIOD)
period = EPGGRAB_OTA_MIN_PERIOD;
return period;
}
static int
epggrab_ota_timeout ( epggrab_ota_mux_t *ota )
{
int timeout = 0;
epggrab_ota_map_t *map;
if (ota->om_timeout)
timeout = ota->om_timeout;
else {
LIST_FOREACH(map, &ota->om_modules, om_link)
if (map->om_timeout > timeout)
timeout = map->om_timeout;
}
if (timeout < EPGGRAB_OTA_MIN_TIMEOUT)
timeout = EPGGRAB_OTA_MIN_TIMEOUT;
return timeout;
}
static void
epggrab_ota_done ( epggrab_ota_mux_t *ota, int cancel, int timeout )
{
LIST_REMOVE(ota, om_q_link);
ota->om_when = dispatch_clock + 10;//epggrab_ota_period(ota);
ota->om_active = 0;
LIST_INSERT_SORTED(&epggrab_ota_pending, ota, om_q_link, om_time_cmp);
/* Re-arm */
if (LIST_FIRST(&epggrab_ota_pending) == ota)
epggrab_ota_pending_timer_cb(NULL);
/* Remove from active */
if (!timeout) {
gtimer_disarm(&epggrab_ota_active_timer);
epggrab_ota_active_timer_cb(NULL);
}
/* Stop mux */
if (!cancel) {
extern const idclass_t mpegts_mux_class;
mpegts_mux_t *mm = mpegts_mux_find(ota->om_mux_uuid);
if (mm) mm->mm_stop(mm, ota, 0);
}
}
/* **************************************************************************
* MPEG-TS listener
* *************************************************************************/
static void
epggrab_mux_start ( mpegts_mux_t *mm, void *p )
{
epggrab_module_t *m;
epggrab_module_ota_t *om;
LIST_FOREACH(m, &epggrab_modules, link) {
if (m->type == EPGGRAB_OTA) {
om = (epggrab_module_ota_t*)m;
if (om->start) om->start(om, dm);
if (om->start) om->start(om, mm);
}
}
}
void epggrab_mux_stop ( dvb_mux_t *dm, int timeout )
static void
epggrab_mux_stop ( mpegts_mux_t *mm, void *p )
{
// 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(&dm->dm_epg_grab);
while (a) {
b = TAILQ_NEXT(a, dm_link);
if (a->dm == dm) {
if (timeout)
epggrab_ota_timeout(a);
else
epggrab_ota_cancel(a);
epggrab_ota_mux_t *ota;
RB_FOREACH(ota, &epggrab_ota_all, om_global_link) {
const char *uuid = idnode_uuid_as_str(&mm->mm_id);
if (!strcmp(ota->om_mux_uuid, uuid))
break;
}
if (ota && ota->om_active)
epggrab_ota_done(ota, 1, 0);
}
void
epggrab_ota_init ( void )
{
static mpegts_listener_t ml = {
.ml_mux_start = epggrab_mux_start,
.ml_mux_stop = epggrab_mux_stop,
};
mpegts_add_listener(&ml);
}
/* **************************************************************************
* Completion handling
* *************************************************************************/
/* **************************************************************************
* Module methods
* *************************************************************************/
epggrab_ota_mux_t *
epggrab_ota_register
( epggrab_module_ota_t *mod, mpegts_mux_t *mm,
int interval, int timeout )
{
static epggrab_ota_mux_t *skel = NULL;
epggrab_ota_map_t *map;
epggrab_ota_mux_t *ota;
/* Find mux entry */
const char *uuid = idnode_uuid_as_str(&mm->mm_id);
if (!skel)
skel = calloc(1, sizeof(epggrab_ota_mux_t));
skel->om_mux_uuid = (char*)uuid;
ota = RB_INSERT_SORTED(&epggrab_ota_all, skel, om_global_link, om_id_cmp);
if (!ota) {
ota = skel;
skel = NULL;
ota->om_mux_uuid = strdup(uuid);
ota->om_active = 1;
// idnode_link(&ota->om_id, NULL);
LIST_INSERT_SORTED(&epggrab_ota_pending, ota, om_q_link, om_time_cmp);
// TODO: save config
// TODO: generic creation routine (grid?)
}
/* Find module entry */
LIST_FOREACH(map, &ota->om_modules, om_link)
if (map->om_module == mod)
break;
if (!mod) {
map = calloc(1, sizeof(epggrab_ota_map_t));
map->om_module = mod;
map->om_timeout = timeout;
map->om_interval = interval;
LIST_INSERT_HEAD(&ota->om_modules, map, om_link);
}
return ota;
}
void
epggrab_ota_complete
( epggrab_module_ota_t *mod, epggrab_ota_mux_t *ota )
{
int done = 1;
epggrab_ota_map_t *map;
/* Just for completion */
LIST_FOREACH(map, &ota->om_modules, om_link) {
if (map->om_module == mod)
map->om_complete = 1;
else if (!map->om_complete)
done = 0;
}
if (!done) return;
/* Done */
epggrab_ota_done(ota, 0, 0);
}
/* **************************************************************************
* Timer callback
* *************************************************************************/
static void
epggrab_ota_active_timer_cb ( void *p )
{
epggrab_ota_mux_t *om = LIST_FIRST(&epggrab_ota_active);
lock_assert(&global_lock);
if (!om)
return;
/* Double check */
if (om->om_when > dispatch_clock)
goto done;
/* Re-queue */
epggrab_ota_done(om, 0, 1);
done:
om = LIST_FIRST(&epggrab_ota_active);
if (om)
gtimer_arm_abs(&epggrab_ota_active_timer, epggrab_ota_active_timer_cb,
NULL, om->om_when);
}
static void
epggrab_ota_pending_timer_cb ( void *p )
{
int r;
epggrab_ota_map_t *map;
epggrab_ota_mux_t *om = LIST_FIRST(&epggrab_ota_pending);
mpegts_mux_t *mm;
lock_assert(&global_lock);
if (!om)
return;
/* Double check */
if (om->om_when > dispatch_clock)
goto done;
LIST_REMOVE(om, om_q_link);
/* Find the mux */
extern const idclass_t mpegts_mux_class;
mm = mpegts_mux_find(om->om_mux_uuid);
if (!mm) {
RB_REMOVE(&epggrab_ota_all, om, om_global_link);
while ((map = LIST_FIRST(&om->om_modules))) {
LIST_REMOVE(map, om_link);
free(map);
}
a = b;
free(om->om_mux_uuid);
free(om);
goto done;
}
}
void epggrab_mux_delete ( dvb_mux_t *dm )
{
epggrab_ota_destroy_by_dm(dm);
}
int epggrab_mux_period ( dvb_mux_t *dm )
{
int period = 0;
epggrab_ota_mux_t *ota;
TAILQ_FOREACH(ota, &dm->dm_epg_grab, dm_link) {
if (!ota->is_reg) continue;
if (ota->timeout > period)
period = ota->timeout;
/* Subscribe to the mux */
r = mm->mm_start(mm, om, "epggrab", 2); // TODO: remove hardcoded
// TODO: need to add src field
if (r) {
om->om_when = dispatch_clock + epggrab_ota_period(om) / 2;
LIST_INSERT_SORTED(&epggrab_ota_pending, om, om_q_link, om_time_cmp);
} else {
om->om_when = dispatch_clock + epggrab_ota_timeout(om);
LIST_INSERT_SORTED(&epggrab_ota_active, om, om_q_link, om_time_cmp);
if (LIST_FIRST(&epggrab_ota_active) == om)
epggrab_ota_active_timer_cb(NULL);
LIST_FOREACH(map, &om->om_modules, om_link)
map->om_complete = 0;
}
return period;
}
dvb_mux_t *epggrab_mux_next ( dvb_network_t *dn )
{
time_t now;
epggrab_ota_mux_t *ota;
time(&now);
TAILQ_FOREACH(ota, &ota_mux_all, glob_link) {
#if TODO_FIX_THIS
if (ota->tdmi->tdmi_adapter != tda) continue;
#endif
if (ota->interval + ota->completed > now) return NULL;
if (!ota->is_reg) return NULL;
if (ota->dm->dm_dn == dn) break;
}
return ota ? ota->dm : NULL;
done:
om = LIST_FIRST(&epggrab_ota_pending);
if (om)
gtimer_arm_abs(&epggrab_ota_pending_timer, epggrab_ota_pending_timer_cb,
NULL, om->om_when);
}
/* **************************************************************************
* Config
* *************************************************************************/
static void _epggrab_ota_load_one ( epggrab_module_ota_t *mod, htsmsg_t *m )
{
htsmsg_t *e;
htsmsg_field_t *f;
int onid, tsid, period, interval;
const char *netname;
HTSMSG_FOREACH(f, m) {
if ((e = htsmsg_get_map_by_field(f))) {
onid = htsmsg_get_u32_or_default(m, "onid", 0);
tsid = htsmsg_get_u32_or_default(m, "tsid", 0);
period = htsmsg_get_u32_or_default(m, "period", 0);
interval = htsmsg_get_u32_or_default(m, "interval", 0);
netname = htsmsg_get_str(m, "networkname");
if (tsid)
epggrab_ota_create_and_register_by_id(mod, onid, tsid,
period, interval,
netname);
}
}
}
void epggrab_ota_load ( void )
{
htsmsg_t *m, *l;
htsmsg_field_t *f;
epggrab_module_t *mod;
if ((m = hts_settings_load("epggrab/otamux"))) {
HTSMSG_FOREACH(f, m) {
mod = epggrab_module_find_by_id(f->hmf_name);
if (!mod || mod->type != EPGGRAB_OTA) continue;
if ((l = htsmsg_get_list_by_field(f)))
_epggrab_ota_load_one((epggrab_module_ota_t*)mod, l);
}
htsmsg_destroy(m);
}
}
static void _epggrab_ota_save_one ( htsmsg_t *m, epggrab_module_ota_t *mod )
{
epggrab_ota_mux_t *ota;
htsmsg_t *e, *l = NULL;
TAILQ_FOREACH(ota, &mod->muxes, grab_link) {
if (!l) l = htsmsg_create_list();
e = htsmsg_create_map();
const dvb_mux_t *dm = ota->dm;
htsmsg_add_u32(e, "onid", dm->dm_network_id);
htsmsg_add_u32(e, "tsid", dm->dm_transport_stream_id);
htsmsg_add_u32(e, "period", ota->timeout);
htsmsg_add_u32(e, "interval", ota->interval);
if (dm->dm_network_name)
htsmsg_add_str(e, "networkname", dm->dm_network_name);
htsmsg_add_msg(l, NULL, e);
}
if (l) htsmsg_add_msg(m, mod->id, l);
}
void epggrab_ota_save ( void )
{
htsmsg_t *m = htsmsg_create_map();
epggrab_module_t *mod;
// TODO: there is redundancy in this saving, because each MUX can
// be represented N times, due to one copy per adapter. But load
// only requires one instance to find them all.
LIST_FOREACH(mod, &epggrab_modules, link) {
if (mod->type != EPGGRAB_OTA) continue;
_epggrab_ota_save_one(m, (epggrab_module_ota_t*)mod);
}
hts_settings_save(m, "epggrab/otamux");
htsmsg_destroy(m);
}
/* **************************************************************************
* 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;
}
/*
* Find existing link
*/
epggrab_ota_mux_t *epggrab_ota_find
( epggrab_module_ota_t *mod, dvb_mux_t *dm )
{
epggrab_ota_mux_t *ota;
TAILQ_FOREACH(ota, &mod->muxes, grab_link) {
if (ota->dm == dm) break;
}
return ota;
}
/*
* Create (temporary) or Find (existing) link
*/
epggrab_ota_mux_t *epggrab_ota_create
( epggrab_module_ota_t *mod, dvb_mux_t *dm)
{
/* Search for existing */
epggrab_ota_mux_t *ota = epggrab_ota_find(mod, dm);
/* Create new */
if (!ota) {
ota = calloc(1, sizeof(epggrab_ota_mux_t));
ota->grab = mod;
ota->dm = dm;
TAILQ_INSERT_TAIL(&ota_mux_all, ota, glob_link);
TAILQ_INSERT_TAIL(&dm->dm_epg_grab, ota, dm_link);
TAILQ_INSERT_TAIL(&mod->muxes, ota, grab_link);
} else {
time_t now;
time(&now);
ota->state = EPGGRAB_OTA_MUX_IDLE;
}
return ota;
}
/*
* Create and register using mux ID
*/
void epggrab_ota_create_and_register_by_id
( epggrab_module_ota_t *mod, int onid, int tsid, int period, int interval,
const char *networkname )
{
dvb_network_t *dn;
dvb_mux_t *dm;
epggrab_ota_mux_t *ota;
LIST_FOREACH(dn, &dvb_networks, dn_global_link) {
LIST_FOREACH(dm, &dn->dn_muxes, dm_network_link) {
if (dm->dm_transport_stream_id != tsid) continue;
if (onid && dm->dm_network_id != onid) continue;
if (networkname && (!dm->dm_network_name ||
strcmp(networkname, dm->dm_network_name))) continue;
ota = epggrab_ota_create(mod, dm);
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->dm->dm_epg_grab, ota, dm_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_dm ( dvb_mux_t *dm )
{
epggrab_ota_mux_t *ota;
while ((ota = TAILQ_FIRST(&dm->dm_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);
epggrab_ota_save();
}
}
/*
* 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);
// Find the last queue entry that can run before ota
// (i.e _ota_time_cmp(ota, entry)>0) and re-insert ota
// directly after this entry. If no matching entry is
// found (i.e ota can run before any other entry),
// re-insert ota at the queue head.
epggrab_ota_mux_t *entry = NULL;
epggrab_ota_mux_t *tmp;
TAILQ_FOREACH(tmp, &ota_mux_all, glob_link) {
if(_ota_time_cmp(ota, tmp)>0) entry = tmp;
}
if (entry) {
TAILQ_INSERT_AFTER(&ota_mux_all, entry, ota, glob_link);
} else {
TAILQ_INSERT_HEAD(&ota_mux_all, ota, glob_link);
}
}
}
int epggrab_ota_begin ( epggrab_ota_mux_t *ota )
{
if (ota->state == EPGGRAB_OTA_MUX_IDLE) {
tvhlog(LOG_DEBUG, ota->grab->id, "begin processing");
ota->state = EPGGRAB_OTA_MUX_RUNNING;
time(&ota->started);
return 1;
}
return 0;
}
void epggrab_ota_complete ( epggrab_ota_mux_t *ota )
{
dvb_mux_t *dm = ota->dm;
if (ota->state != EPGGRAB_OTA_MUX_COMPLETE) {
tvhlog(LOG_DEBUG, ota->grab->id, "processing complete");
ota->state = EPGGRAB_OTA_MUX_COMPLETE;
time(&ota->completed);
/* Check others */
TAILQ_FOREACH(ota, &dm->dm_epg_grab, dm_link) {
if (ota->is_reg && ota->state == EPGGRAB_OTA_MUX_RUNNING) break;
}
#if 0 // XXX(dvbreorg)
/* All complete (bring timer forward) */
if (!ota) {
dvb_network_t *dn = dm->dm_dn;
gtimer_arm(&dn->dn_mux_scanner_timer,
dvb_network_mux_scanner, dn, 20);
}
#endif
}
}
/* Reset */
void epggrab_ota_cancel ( epggrab_ota_mux_t *ota )
{
if (ota->state == EPGGRAB_OTA_MUX_RUNNING) {
tvhlog(LOG_DEBUG, ota->grab->id, "processing cancelled");
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);
_epggrab_ota_finished(ota);
}
/*
* Check status
*/
int epggrab_ota_is_complete ( epggrab_ota_mux_t *ota )
{
if (ota->state == EPGGRAB_OTA_MUX_COMPLETE ||
ota->state == EPGGRAB_OTA_MUX_TIMEDOUT) {
if (epggrab_ota_is_blocked(ota))
return 1;
ota->state = EPGGRAB_OTA_MUX_IDLE;
}
return 0;
}
int epggrab_ota_is_blocked ( epggrab_ota_mux_t *ota )
{
time_t t;
time(&t);
return t < (ota->completed + ota->interval);
}

View file

@ -19,6 +19,8 @@
#ifndef __EPGGRAB_PRIVATE_H__
#define __EPGGRAB_PRIVATE_H__
struct mpegts_mux;
/* **************************************************************************
* Generic module routines
* *************************************************************************/
@ -88,7 +90,7 @@ epggrab_module_ota_t *epggrab_module_ota_create
( epggrab_module_ota_t *skel,
const char *id, const char *name, int priority,
void (*start) (epggrab_module_ota_t*m,
struct dvb_mux *dm),
struct mpegts_mux *mm),
int (*enable) (void *m, uint8_t e ),
epggrab_channel_tree_t *channels );
@ -109,11 +111,11 @@ void epggrab_ota_save ( void );
* blocked (i.e. has completed within interval period)
*/
epggrab_ota_mux_t *epggrab_ota_find
( epggrab_module_ota_t *mod, struct dvb_mux *dm );
( epggrab_module_ota_t *mod, struct mpegts_mux *dm );
epggrab_ota_mux_t *epggrab_ota_create
( epggrab_module_ota_t *mod, struct dvb_mux *dm );
( epggrab_module_ota_t *mod, struct mpegts_mux *dm );
void epggrab_ota_create_and_register_by_id
( epggrab_module_ota_t *mod, int nid, int tsid,
( epggrab_module_ota_t *mod, uint16_t onid, uint16_t tsid,
int period, int interval, const char *name );
/*
@ -121,27 +123,24 @@ void epggrab_ota_create_and_register_by_id
*/
void epggrab_ota_destroy ( epggrab_ota_mux_t *ota );
void epggrab_ota_destroy_by_module ( epggrab_module_ota_t *mod );
#if 0
void epggrab_ota_destroy_by_dm ( struct dvb_mux *dm );
#endif
/*
* Register interest
* In module functions
*/
void epggrab_ota_register
( epggrab_ota_mux_t *ota, int timeout, int interval );
epggrab_ota_mux_t *epggrab_ota_register
( epggrab_module_ota_t *mod, struct mpegts_mux *mux,
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 );
void epggrab_ota_complete
( epggrab_module_ota_t *mod, 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 );
/* **************************************************************************
* Miscellaneous

View file

@ -28,8 +28,7 @@
#include <unistd.h>
#include "tvheadend.h"
#include "channels.h"
#include "dvb/dvb.h"
#include "dvb/dvb_support.h"
#include "input/mpegts/dvb.h"
#include "service.h"
#include "epg.h"
#include "epggrab.h"

View file

@ -482,6 +482,9 @@ extern const idclass_t mpegts_network_class;
#define mpegts_network_find(u)\
idnode_find(u, &mpegts_network_class)
mpegts_mux_t *mpegts_network_find_mux
(mpegts_network_t *mn, uint16_t onid, uint16_t tsid);
void mpegts_network_delete ( mpegts_network_t *mn );
@ -520,6 +523,8 @@ mpegts_mux_instance_t *mpegts_mux_instance_create0
( mpegts_mux_instance_t *mmi, const idclass_t *class, const char *uuid,
mpegts_input_t *mi, mpegts_mux_t *mm );
mpegts_service_t *mpegts_mux_find_service(mpegts_mux_t *ms, uint16_t sid);
#define mpegts_mux_instance_create(type, uuid, mi, mm)\
(struct type*)mpegts_mux_instance_create0(calloc(1, sizeof(struct type)),\
&type##_class, uuid,\
@ -579,6 +584,35 @@ void mpegts_service_save ( mpegts_service_t *s, htsmsg_t *c );
void mpegts_service_delete ( service_t *s );
/*
* MPEG-TS event handler
*/
typedef struct mpegts_listener
{
LIST_ENTRY(mpegts_listener) ml_link;
void *ml_opaque;
void (*ml_mux_start) (mpegts_mux_t *mm, void *p);
void (*ml_mux_stop) (mpegts_mux_t *mm, void *p);
void (*ml_mux_create) (mpegts_mux_t *mm, void *p);
void (*ml_mux_delete) (mpegts_mux_t *mm, void *p);
} mpegts_listener_t;
LIST_HEAD(,mpegts_listener) mpegts_listeners;
#define mpegts_add_listener(ml)\
LIST_INSERT_HEAD(&mpegts_listeners, ml, ml_link)
#define mpegts_rem_listener(ml)\
LIST_REMOVE(ml, ml_link)
#define mpegts_fire_event(t, op)\
{\
mpegts_listener_t *ml;\
LIST_FOREACH(ml, &mpegts_listeners, ml_link)\
if (ml->op) ml->op(t, ml->ml_opaque);\
} (void)0
#endif /* __TVH_MPEGTS_H__ */
/******************************************************************************

View file

@ -112,9 +112,9 @@ int dvb_get_string_with_len
#define bcdtoint(i) ((((i & 0xf0) >> 4) * 10) + (i & 0x0f))
time_t dvb_convert_date(uint8_t *dvb_buf);
time_t dvb_convert_date(const uint8_t *dvb_buf);
void atsc_utf16_to_utf8(uint8_t *src, int len, char *buf, int buflen);
void atsc_utf16_to_utf8(const uint8_t *src, int len, char *buf, int buflen);
/*
* PSI processing

View file

@ -19,7 +19,7 @@
#include <string.h>
#include "tvheadend.h"
#include "settings.h"
#include "dvb/dvb_charset.h"
#include "dvb_charset.h"
static LIST_HEAD(,dvb_charset) dvb_charset_list;
/*

View file

@ -449,7 +449,7 @@ dvb_table_complete
return 0;
}
static int
int
dvb_table_end
(mpegts_table_t *mt, mpegts_table_state_t *st, int sect)
{
@ -476,7 +476,7 @@ dvb_table_end
/*
* Begin table
*/
static int
int
dvb_table_begin
(mpegts_table_t *mt, const uint8_t *ptr, int len,
int tableid, int extraid, int minlen,

View file

@ -310,7 +310,7 @@ dvb_get_string_with_len(char *dst, size_t dstlen,
*
*/
void
atsc_utf16_to_utf8(uint8_t *src, int len, char *buf, int buflen)
atsc_utf16_to_utf8(const uint8_t *src, int len, char *buf, int buflen)
{
int i, c, r;
@ -331,7 +331,7 @@ atsc_utf16_to_utf8(uint8_t *src, int len, char *buf, int buflen)
*/
time_t
dvb_convert_date(uint8_t *dvb_buf)
dvb_convert_date(const uint8_t *dvb_buf)
{
int i;
int year, month, day, hour, min, sec;

View file

@ -1,91 +0,0 @@
/*
* TV Input - Linux DVB interface - Support
* Copyright (C) 2007 Andreas Öman
*
* 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/>.
*/
/*
* Based on:
*
* ITU-T Recommendation H.222.0 / ISO standard 13818-1
* EN 300 468 - V1.7.1
*/
#ifndef DVB_SUPPORT_H
#define DVB_SUPPORT_H
#include "dvb.h"
#define DVB_DESC_VIDEO_STREAM 0x02
#define DVB_DESC_REGISTRATION 0x05
#define DVB_DESC_CA 0x09
#define DVB_DESC_LANGUAGE 0x0a
/* Descriptors defined in EN 300 468 */
#define DVB_DESC_NETWORK_NAME 0x40
#define DVB_DESC_SERVICE_LIST 0x41
#define DVB_DESC_SAT 0x43
#define DVB_DESC_CABLE 0x44
#define DVB_DESC_SHORT_EVENT 0x4d
#define DVB_DESC_EXT_EVENT 0x4e
#define DVB_DESC_SERVICE 0x48
#define DVB_DESC_COMPONENT 0x50
#define DVB_DESC_CONTENT 0x54
#define DVB_DESC_PARENTAL_RAT 0x55
#define DVB_DESC_TELETEXT 0x56
#define DVB_DESC_SUBTITLE 0x59
#define DVB_DESC_TERR 0x5a
#define DVB_DESC_AC3 0x6a
#define DVB_DESC_DEF_AUTHORITY 0x73
#define DVB_DESC_CRID 0x76
#define DVB_DESC_EAC3 0x7a
#define DVB_DESC_AAC 0x7c
#define DVB_DESC_LOCAL_CHAN 0x83
typedef struct dvb_string_conv
{
uint8_t type;
size_t (*func) ( char *dst, size_t *dstlen,
const uint8_t* src, size_t srclen );
} dvb_string_conv_t;
int dvb_get_string(char *dst, size_t dstlen, const uint8_t *src,
const size_t srclen, const char *dvb_charset,
dvb_string_conv_t *conv);
int dvb_get_string_with_len(char *dst, size_t dstlen,
const uint8_t *buf, size_t buflen, const char *dvb_charset,
dvb_string_conv_t *conv);
#define bcdtoint(i) ((((i & 0xf0) >> 4) * 10) + (i & 0x0f))
time_t dvb_convert_date(uint8_t *dvb_buf);
const char *dvb_adaptertype_to_str(int type);
int dvb_str_to_adaptertype(const char *str);
const char *dvb_polarisation_to_str(int pol);
const char *dvb_polarisation_to_str_long(int pol);
th_dvb_adapter_t *dvb_adapter_find_by_identifier(const char *identifier);
th_dvb_mux_instance_t *dvb_mux_find_by_identifier(const char *identifier);
int dvb_mux_badness(th_dvb_mux_instance_t *tdmi);
const char *dvb_mux_status(th_dvb_mux_instance_t *tdmi);
const char *dvb_mux_nicefreq(const dvb_mux_t *dm);
const char *dvb_mux_nicename(const dvb_mux_t *dm);
void atsc_utf16_to_utf8(uint8_t *src, int len, char *buf, int buflen);
#endif /* DVB_SUPPORT_H */

View file

@ -98,14 +98,14 @@ mpegts_input_current_weight ( mpegts_input_t *mi )
const th_subscription_t *ths;
int w = 0;
// TODO: we probably need a way for mux level subs
/* Check for scan (weight 1) */
LIST_FOREACH(mmi, &mi->mi_mux_active, mmi_active_link) {
RB_FOREACH(mms, &mmi->mmi_subs, mms_link)
w = MAX(w, mms->mms_weight);
}
/* Check for mux subs */
/* Check subscriptions */
pthread_mutex_lock(&mi->mi_delivery_mutex);
LIST_FOREACH(s, &mi->mi_transports, s_active_link) {

View file

@ -392,6 +392,8 @@ mpegts_mux_start
tvhdebug("mpegts", "%s - no free input (fail=%d)", buf, fail);
return SM_CODE_NO_FREE_ADAPTER;
}
mpegts_fire_event(mm, ml_mux_start);
return 0;
}
@ -466,6 +468,8 @@ mpegts_mux_stop ( mpegts_mux_t *mm, void *src, int force )
mpegts_network_schedule_initial_scan(mn);
}
mpegts_fire_event(mm, ml_mux_stop);
/* Clear */
mm->mm_active = NULL;
}
@ -656,6 +660,20 @@ mpegts_mux_set_crid_authority ( mpegts_mux_t *mm, const char *defauth )
return 1;
}
/* **************************************************************************
* Search
* *************************************************************************/
mpegts_service_t *
mpegts_mux_find_service ( mpegts_mux_t *mm, uint16_t sid)
{
mpegts_service_t *ms;
LIST_FOREACH(ms, &mm->mm_services, s_dvb_mux_link)
if (ms->s_dvb_service_id == sid)
break;
return ms;
}
/******************************************************************************
* Editor Configuration
*

View file

@ -352,6 +352,23 @@ mpegts_network_build
return NULL;
}
/******************************************************************************
* Search
*****************************************************************************/
mpegts_mux_t *
mpegts_network_find_mux
( mpegts_network_t *mn, uint16_t onid, uint16_t tsid )
{
mpegts_mux_t *mm;
LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link) {
if (mm->mm_onid && onid && mm->mm_onid != onid) continue;
if (mm->mm_tsid == tsid)
break;
}
return mm;
}
/******************************************************************************
* Editor Configuration
*

View file

@ -827,7 +827,7 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque)
if(!strcmp(components[0], "channelid")) {
ch = channel_find_by_id(atoi(components[1]));
} else if(!strcmp(components[0], "channel")) {
ch = channel_find_by_name(components[1]);
ch = channel_find(components[1]);
} else if(!strcmp(components[0], "service")) {
service = service_find_by_identifier(components[1]);
#if 0//ENABLE_LINUXDVB