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:
parent
c022ab7c75
commit
df44bad3e9
18 changed files with 504 additions and 855 deletions
20
Makefile
20
Makefile
|
@ -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} += \
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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, §, &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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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__ */
|
||||
|
||||
/******************************************************************************
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
/*
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue