mpegts: new feature for scheduling mux tuning events

Could be useful for requesting periodic tuning of mux for EMM harvesting.
Will eventually be used to replace the current OTA EPG scheduling.
This commit is contained in:
Adam Sutton 2014-04-21 19:33:17 +01:00
parent 99aee928c2
commit eeae897dea
8 changed files with 490 additions and 0 deletions

View file

@ -178,6 +178,7 @@ SRCS-$(CONFIG_MPEGTS) += \
src/input/mpegts/dvb_charset.c \
src/input/mpegts/dvb_psi.c \
src/input/mpegts/tsdemux.c \
src/input/mpegts/mpegts_mux_sched.c \
# MPEGTS DVB
SRCS-${CONFIG_MPEGTS_DVB} += \

View file

@ -251,6 +251,43 @@ api_mpegts_service_grid
}
}
/*
* Mux scheduler
*/
static void
api_mpegts_mux_sched_grid
( idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
{
mpegts_mux_sched_t *mms;
LIST_FOREACH(mms, &mpegts_mux_sched_all, mms_link)
idnode_set_add(ins, (idnode_t*)mms, &conf->filter);
}
static int
api_mpegts_mux_sched_create
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
int err;
htsmsg_t *conf;
mpegts_mux_sched_t *mms;
if (!(conf = htsmsg_get_map(args, "conf")))
return EINVAL;
pthread_mutex_lock(&global_lock);
mms = mpegts_mux_sched_create(NULL, conf);
if (mms) {
err = 0;
*resp = htsmsg_create_map();
mpegts_mux_sched_save(mms);
} else {
err = EINVAL;
}
pthread_mutex_unlock(&global_lock);
return err;
}
#if ENABLE_MPEGTS_DVB
static int
api_dvb_scanfile_list
@ -321,6 +358,9 @@ api_mpegts_init ( void )
{ "mpegts/mux/class", ACCESS_ANONYMOUS, api_idnode_class, (void*)&mpegts_mux_class },
{ "mpegts/service/grid", ACCESS_ANONYMOUS, api_idnode_grid, api_mpegts_service_grid },
{ "mpegts/service/class", ACCESS_ANONYMOUS, api_idnode_class, (void*)&mpegts_service_class },
{ "mpegts/mux_sched/class", ACCESS_ANONYMOUS, api_idnode_class, (void*)&mpegts_mux_sched_class },
{ "mpegts/mux_sched/grid", ACCESS_ANONYMOUS, api_idnode_grid, api_mpegts_mux_sched_grid },
{ "mpegts/mux_sched/create", ACCESS_ANONYMOUS, api_mpegts_mux_sched_create, NULL },
#if ENABLE_MPEGTS_DVB
{ "dvb/scanfile/list", ACCESS_ANONYMOUS, api_dvb_scanfile_list, NULL },
#endif

View file

@ -115,6 +115,7 @@ void tvh_input_stream_destroy ( tvh_input_stream_t *st );
#if ENABLE_MPEGTS
#include "input/mpegts.h"
#include "input/mpegts/mpegts_mux_sched.h"
#if ENABLE_MPEGTS_DVB
#include "input/mpegts/mpegts_dvb.h"
#endif

View file

@ -0,0 +1,342 @@
/*
* Tvheadend - TS file input system
*
* Copyright (C) 2014 Adam Sutton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tvheadend.h"
#include "input.h"
#include "input/mpegts/mpegts_mux_sched.h"
#include "streaming.h"
#include "settings.h"
static void mpegts_mux_sched_timer ( void *p );
static void mpegts_mux_sched_input ( void *p, streaming_message_t *sm );
mpegts_mux_sched_list_t mpegts_mux_sched_all;
/******************************************************************************
* Class
*****************************************************************************/
static void
mpegts_mux_sched_set_timer ( mpegts_mux_sched_t *mms )
{
/* Upate timer */
if (!mms->mms_enabled) {
if (mms->mms_sub)
subscription_unsubscribe(mms->mms_sub);
mms->mms_sub = NULL;
mms->mms_active = 0;
gtimer_disarm(&mms->mms_timer);
} else if (mms->mms_active) {
if (mms->mms_timeout <= 0)
gtimer_disarm(&mms->mms_timer);
else {
gtimer_arm(&mms->mms_timer, mpegts_mux_sched_timer, mms,
mms->mms_timeout);
}
} else {
time_t now, nxt;
time(&now);
if (!cron_next(&mms->mms_cronjob, now, &nxt)) {
gtimer_arm_abs(&mms->mms_timer, mpegts_mux_sched_timer, mms, nxt);
}
}
}
static void
mpegts_mux_sched_class_save ( idnode_t *in )
{
mpegts_mux_sched_t *mms = (mpegts_mux_sched_t*)in;
/* Update timer */
mpegts_mux_sched_set_timer(mms);
/* Save */
mpegts_mux_sched_save(mms);
}
static void
mpegts_mux_sched_class_delete ( idnode_t *in )
{
mpegts_mux_sched_delete((mpegts_mux_sched_t*)in, 1);
}
static htsmsg_t *
mpegts_mux_sched_class_mux_list ( void *o )
{
htsmsg_t *m, *p;
p = htsmsg_create_map();
htsmsg_add_str (p, "class", "mpegts_mux");
htsmsg_add_bool(p, "enum", 1);
m = htsmsg_create_map();
htsmsg_add_str (m, "type", "api");
htsmsg_add_str (m, "uri", "idnode/load");
htsmsg_add_str (m, "event", "mpegts_mux");
htsmsg_add_msg (m, "params", p);
return m;
}
static int
mpegts_mux_sched_class_cron_set ( void *p, const void *v )
{
mpegts_mux_sched_t *mms = p;
const char *str = v;
if (strcmp(str, mms->mms_cronstr ?: "")) {
if (!cron_set(&mms->mms_cronjob, str)) {
free(mms->mms_cronstr);
mms->mms_cronstr = strdup(str);
return 1;
} else {
tvhwarn("muxsched", "invalid cronjob spec (%s)", str);
}
}
return 0;
}
const idclass_t mpegts_mux_sched_class =
{
.ic_class = "mpegts_mux_sched",
.ic_caption = "Mux Sched Entry",
.ic_event = "mpegts_mux_sched",
.ic_save = mpegts_mux_sched_class_save,
.ic_delete = mpegts_mux_sched_class_delete,
.ic_properties = (const property_t[]){
{
.type = PT_BOOL,
.id = "enabled",
.name = "Enabled",
.off = offsetof(mpegts_mux_sched_t, mms_enabled),
.def.i = 1,
},
{
.type = PT_STR,
.id = "mux",
.name = "Mux",
.off = offsetof(mpegts_mux_sched_t, mms_mux),
.list = mpegts_mux_sched_class_mux_list,
},
{
.type = PT_STR,
.id = "cron",
.name = "Cron",
.off = offsetof(mpegts_mux_sched_t, mms_cronstr),
.set = mpegts_mux_sched_class_cron_set,
},
{
.type = PT_INT,
.id = "timeout",
.name = "Timout (secs)",
.off = offsetof(mpegts_mux_sched_t, mms_timeout),
},
{
},
}
};
/******************************************************************************
* Input
*****************************************************************************/
static void
mpegts_mux_sched_input ( void *p, streaming_message_t *sm )
{
mpegts_mux_sched_t *mms = p;
switch (sm->sm_type) {
case SMT_STOP:
gtimer_arm(&mms->mms_timer, mpegts_mux_sched_timer, mms, 0);
break;
default:
// ignore
break;
}
streaming_msg_free(sm);
}
/******************************************************************************
* Timer
*****************************************************************************/
static void
mpegts_mux_sched_timer ( void *p )
{
mpegts_mux_t *mm;
mpegts_mux_sched_t *mms = p;
time_t now, nxt;
/* Not enabled (shouldn't be running) */
if (!mms->mms_enabled)
return;
/* Invalid config (creating?) */
if (!mms->mms_mux)
return;
/* Find mux */
if (!(mm = mpegts_mux_find(mms->mms_mux))) {
tvhdebug("muxsched", "mux has been removed, delete sched entry");
mpegts_mux_sched_delete(mms, 1);
return;
}
/* Current time */
time(&now);
/* Start sub */
if (!mms->mms_active) {
assert(mms->mms_sub == NULL);
mms->mms_sub
= subscription_create_from_mux(mm, mms->mms_weight,
mms->mms_creator ?: "",
&mms->mms_input,
SUBSCRIPTION_NONE,
NULL, NULL, NULL, NULL);
/* Failed (try-again soon) */
if (!mms->mms_sub) {
gtimer_arm(&mms->mms_timer, mpegts_mux_sched_timer, mms, 60);
/* OK */
} else {
mms->mms_active = 1;
if (mms->mms_timeout > 0) {
gtimer_arm(&mms->mms_timer, mpegts_mux_sched_timer, mms,
mms->mms_timeout);
}
}
/* Cancel sub */
} else {
if (mms->mms_sub) {
subscription_unsubscribe(mms->mms_sub);
mms->mms_sub = NULL;
}
mms->mms_active = 0;
/* Find next */
if (cron_next(&mms->mms_cronjob, now, &nxt)) {
tvherror("muxsched", "failed to find next event");
return;
}
/* Timer */
gtimer_arm_abs(&mms->mms_timer, mpegts_mux_sched_timer, mms, nxt);
}
}
/******************************************************************************
* Init / Create
*****************************************************************************/
mpegts_mux_sched_t *
mpegts_mux_sched_create ( const char *uuid, htsmsg_t *conf )
{
mpegts_mux_sched_t *mms;
if (!(mms = calloc(1, sizeof(mpegts_mux_sched_t)))) {
tvherror("muxsched", "calloc() failed");
assert(0);
return NULL;
}
/* Insert node */
idnode_insert(&mms->mms_id, uuid, &mpegts_mux_sched_class);
/* Add to list */
LIST_INSERT_HEAD(&mpegts_mux_sched_all, mms, mms_link);
/* Initialise */
streaming_target_init(&mms->mms_input, mpegts_mux_sched_input, mms, 0);
/* Load conf */
if (conf)
idnode_load(&mms->mms_id, conf);
/* Validate */
if (!mpegts_mux_find(mms->mms_mux ?: "") ||
!mms->mms_cronstr) {
mpegts_mux_sched_delete(mms, 1);
return NULL;
}
/* Set timer */
mpegts_mux_sched_set_timer(mms);
return mms;
}
void
mpegts_mux_sched_save ( mpegts_mux_sched_t *mms )
{
htsmsg_t *c = htsmsg_create_map();
idnode_save(&mms->mms_id, c);
hts_settings_save(c, "muxsched/%s", idnode_uuid_as_str(&mms->mms_id));
htsmsg_destroy(c);
}
void
mpegts_mux_sched_delete ( mpegts_mux_sched_t *mms, int delconf )
{
LIST_REMOVE(mms, mms_link);
if (delconf)
hts_settings_remove("muxsched/%s", idnode_uuid_as_str(&mms->mms_id));
if (mms->mms_sub)
subscription_unsubscribe(mms->mms_sub);
gtimer_disarm(&mms->mms_timer);
idnode_unlink(&mms->mms_id);
free(mms->mms_cronstr);
free(mms->mms_mux);
free(mms->mms_creator);
free(mms);
}
void
mpegts_mux_sched_init ( void )
{
htsmsg_t *c, *e;
htsmsg_field_t *f;
/* Load settings */
if ((c = hts_settings_load_r(1, "muxsched"))) {
HTSMSG_FOREACH(f, c) {
if (!(e = htsmsg_field_get_map(f))) continue;
mpegts_mux_sched_create(f->hmf_name, e);
}
htsmsg_destroy(c);
}
}
void
mpegts_mux_sched_done ( void )
{
mpegts_mux_sched_t *mms;
pthread_mutex_lock(&global_lock);
while ((mms = LIST_FIRST(&mpegts_mux_sched_all)))
mpegts_mux_sched_delete(mms, 0);
pthread_mutex_unlock(&global_lock);
}
/******************************************************************************
* Editor Configuration
*
* vim:sts=2:ts=2:sw=2:et
*****************************************************************************/

View file

@ -0,0 +1,79 @@
/*
* Tvheadend - TS file input system
*
* Copyright (C) 2014 Adam Sutton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TVH_MPEGTS_MUX_SCHED_H__
#define __TVH_MPEGTS_MUX_SCHED_H__
#include "tvheadend.h"
#include "cron.h"
#include "idnode.h"
#include "subscriptions.h"
typedef LIST_HEAD(,mpegts_mux_sched) mpegts_mux_sched_list_t;
extern mpegts_mux_sched_list_t mpegts_mux_sched_all;
extern const idclass_t mpegts_mux_sched_class;
typedef struct mpegts_mux_sched
{
idnode_t mms_id;
LIST_ENTRY(mpegts_mux_sched) mms_link;
/*
* Configuration
*/
int mms_enabled; ///< Enabled
char *mms_cronstr; ///< Cron configuration string
char *mms_mux; ///< Mux UUID
char *mms_creator; ///< Creator of entry
int mms_timeout; ///< Timeout (in seconds)
int mms_weight; ///< Weighting
/*
* Cron handling
*/
int mms_active; ///< Subscription is active
gtimer_t mms_timer; ///< Timer for start/end
cron_t mms_cronjob; ///< Cron spec
/*
* Subscription
*/
th_subscription_t *mms_sub; ///< Subscription handler
streaming_target_t mms_input; ///< Streaming input
} mpegts_mux_sched_t;
mpegts_mux_sched_t *mpegts_mux_sched_create ( const char *uuid, htsmsg_t *c );
void mpegts_mux_sched_delete ( mpegts_mux_sched_t *mms, int delconf );
void mpegts_mux_sched_save ( mpegts_mux_sched_t *mms );
void mpegts_mux_sched_init ( void );
void mpegts_mux_sched_done ( void );
#endif /* __TVH_MPEGTS_H__ */
/******************************************************************************
* Editor Configuration
*
* vim:sts=2:ts=2:sw=2:et
*****************************************************************************/

View file

@ -771,6 +771,9 @@ main(int argc, char **argv)
#if ENABLE_LINUXDVB
linuxdvb_init(adapter_mask);
#endif
#if ENABLE_MPEGTS
mpegts_mux_sched_init();
#endif
channel_init();
@ -837,6 +840,9 @@ main(int argc, char **argv)
tvhftrace("main", webui_done);
tvhftrace("main", http_client_done);
tvhftrace("main", fsmonitor_done);
#if ENABLE_MPEGTS
tvhftrace("main", mpegts_mux_sched_done);
#endif
#if ENABLE_MPEGTS_DVB
tvhftrace("main", dvb_network_done);
#endif

View file

@ -137,3 +137,23 @@ tvheadend.services = function(panel)
}
});
}
tvheadend.mux_sched = function(panel)
{
tvheadend.idnode_grid(panel, {
url : 'api/mpegts/mux_sched',
comet : 'mpegts_mux_sched',
titleS : 'Mux Scheduler',
titleP : 'Mux Schedulers',
tabIndex : 4,
hidemode : true,
add : {
url : 'api/mpegts/mux_sched',
titleS : 'Mux Scheduler',
create : {
url : 'api/mpegts/mux_sched/create'
}
},
del : true
});
}

View file

@ -249,6 +249,7 @@ function accessUpdate(o) {
tvheadend.networks(tvheadend.conf_dvbin);
tvheadend.muxes(tvheadend.conf_dvbin);
tvheadend.services(tvheadend.conf_dvbin);
tvheadend.mux_sched(tvheadend.conf_dvbin);
tabs1.push(tvheadend.conf_dvbin);
/* Channel / EPG */