status: added back in basic status info

It will now show subscriptions and streams (tuners/muxes combined).
This commit is contained in:
Adam Sutton 2013-09-06 22:49:22 +01:00
parent f16c29559e
commit 8ac1b3d7ed
14 changed files with 314 additions and 84 deletions

View file

@ -109,10 +109,12 @@ SRCS = src/version.c \
src/tvhtime.c \
src/descrambler/descrambler.c \
src/service_mapper.c \
src/input.c \
SRCS += \
src/api.c \
src/api/api_idnode.c \
src/api/api_input.c \
src/api/api_channel.c \
src/api/api_service.c \
src/api/api_mpegts.c \

View file

@ -116,6 +116,7 @@ void api_init ( void )
/* Subsystems */
api_idnode_init();
api_input_init();
api_mpegts_init();
api_service_init();
api_channel_init();

View file

@ -57,9 +57,10 @@ int api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp );
*/
void api_init ( void );
void api_idnode_init ( void );
void api_mpegts_init ( void );
void api_input_init ( void );
void api_service_init ( void );
void api_channel_init ( void );
void api_mpegts_init ( void );
/*
* IDnode

71
src/api/api_input.c Normal file
View file

@ -0,0 +1,71 @@
/*
* API - channel related calls
*
* Copyright (C) 2013 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_API_INPUT_H__
#define __TVH_API_INPUT_H__
#include "tvheadend.h"
#include "idnode.h"
#include "input.h"
#include "access.h"
#include "api.h"
static int
api_input_status
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
int c = 0;
htsmsg_t *l, *e;
tvh_input_t *ti;
tvh_input_stream_t *st;
tvh_input_stream_list_t stl = { 0 };
TVH_INPUT_FOREACH(ti)
ti->ti_get_streams(ti, &stl);
l = htsmsg_create_list();
while ((st = LIST_FIRST(&stl))) {
e = tvh_input_stream_create_msg(st);
htsmsg_add_msg(l, NULL, e);
tvh_input_stream_destroy(st);
LIST_REMOVE(st, link);
free(st);
c++;
}
*resp = htsmsg_create_map();
htsmsg_add_msg(*resp, "entries", l);
htsmsg_add_u32(*resp, "totalCount", c);
return 0;
}
void api_input_init ( void )
{
static api_hook_t ah[] = {
{ "input/status", ACCESS_ANONYMOUS, api_input_status, NULL },
// { "input/tree", ACCESS_ANONYMOUS, NULL, NULL },
{ NULL },
};
api_register_all(ah);
}
#endif /* __TVH_API_INPUT_H__ */

View file

@ -654,7 +654,7 @@ capmt_table_input(struct th_descrambler *td, struct service *s,
return;
if (!t->s_dvb_active_input) return;
lfe = (linuxdvb_frontend_t*)t->s_dvb_active_input;
if (!idnode_is_instance(&lfe->mi_id, &linuxdvb_frontend_class))
if (!idnode_is_instance(&lfe->ti_id, &linuxdvb_frontend_class))
return;
adapter_num = ((linuxdvb_adapter_t*)lfe->lh_parent)->la_number;
@ -893,7 +893,7 @@ capmt_service_start(service_t *s)
return;
if (!t->s_dvb_active_input) return;
lfe = (linuxdvb_frontend_t*)t->s_dvb_active_input;
if (!idnode_is_instance(&lfe->mi_id, &linuxdvb_frontend_class))
if (!idnode_is_instance(&lfe->ti_id, &linuxdvb_frontend_class))
return;
tuner = ((linuxdvb_adapter_t*)lfe->lh_parent)->la_number;

View file

@ -19,6 +19,61 @@
#ifndef __TVH_INPUT_H__
#define __TVH_INPUT_H__
#include "idnode.h"
#include "queue.h"
/*
* Input stream structure - used for getting statistics about active streams
*/
typedef struct tvh_input_stream_stats
{
int signal; ///< Signal level (0-100)
int ber; ///< Bit error rate (0-100?)
int unc; ///< Uncorrectable errors
int snr; ///< Signal 2 Noise (dB)
int bps; ///< Bandwidth (bps)
} tvh_input_stream_stats_t;
typedef struct tvh_input_stream {
LIST_ENTRY(tvh_input_stream) link;
char *uuid; ///< Unique ID of the entry (used for updates)
char *input_name; ///< Name of the parent input
char *stream_name; ///< Name for this stream
int subs_count; ///< Number of subcscriptions
int max_weight; ///< Current max weight
tvh_input_stream_stats_t stats;
} tvh_input_stream_t;
typedef LIST_HEAD(,tvh_input_stream) tvh_input_stream_list_t;
/*
* Generic input super-class
*/
typedef struct tvh_input {
idnode_t ti_id;
LIST_ENTRY(tvh_input) ti_link;
void (*ti_get_streams) (struct tvh_input *, tvh_input_stream_list_t*);
} tvh_input_t;
typedef LIST_HEAD(,tvh_input) tvh_input_list_t;
tvh_input_list_t tvh_inputs;
#define TVH_INPUT_FOREACH(x) LIST_FOREACH(x, &tvh_inputs, ti_link)
void input_init ( void );
htsmsg_t * tvh_input_stream_create_msg ( tvh_input_stream_t *st );
void tvh_input_stream_destroy ( tvh_input_stream_t *st );
#if ENABLE_MPEGPS
#include "input/mpegps.h"
#endif
@ -36,6 +91,4 @@
#endif
#endif
void input_init ( void );
#endif /* __TVH_INPUT_H__ */

View file

@ -20,6 +20,7 @@
#ifndef __TVH_MPEGTS_H__
#define __TVH_MPEGTS_H__
#include "input.h"
#include "service.h"
#include "mpegts/dvb.h"
@ -374,8 +375,9 @@ struct mpegts_mux_instance
LIST_HEAD(,th_subscription) mmi_subs;
// TODO: remove this
int mmi_tune_failed; // this is really DVB
tvh_input_stream_stats_t mmi_stats;
int mmi_tune_failed;
void (*mmi_delete) (mpegts_mux_instance_t *mmi);
};
@ -390,7 +392,7 @@ struct mpegts_mux_sub
/* Input source */
struct mpegts_input
{
idnode_t mi_id;
tvh_input_t;
int mi_enabled;
@ -407,6 +409,11 @@ struct mpegts_input
LIST_HEAD(,mpegts_mux_instance) mi_mux_instances;
/*
* Status
*/
gtimer_t mi_status_timer;
/*
* Input processing
*/
@ -416,8 +423,6 @@ struct mpegts_input
LIST_HEAD(,service) mi_transports;
int mi_bytes;
struct mpegts_table_feed_queue mi_table_feed;
pthread_cond_t mi_table_feed_cond; // Bound to mi_delivery_mutex
@ -484,6 +489,8 @@ void mpegts_input_set_network ( mpegts_input_t *mi, mpegts_network_t *mn );
void mpegts_input_open_service ( mpegts_input_t *mi, mpegts_service_t *s, int init );
void mpegts_input_close_service ( mpegts_input_t *mi, mpegts_service_t *s );
void mpegts_input_status_timer ( void *p );
void mpegts_network_register_builder
( const idclass_t *idc,
mpegts_network_t *(*build)(const idclass_t *idc, htsmsg_t *conf) );

View file

@ -71,7 +71,7 @@ linuxdvb_adapter_save ( linuxdvb_adapter_t *la, htsmsg_t *m )
htsmsg_t *l;
linuxdvb_hardware_t *lh;
idnode_save(&la->mi_id, m);
idnode_save(&la->ti_id, m);
htsmsg_add_u32(m, "number", la->la_number);
if (la->la_rootpath)
htsmsg_add_str(m, "rootpath", la->la_rootpath);
@ -81,7 +81,7 @@ linuxdvb_adapter_save ( linuxdvb_adapter_t *la, htsmsg_t *m )
LIST_FOREACH(lh, &la->lh_children, lh_parent_link) {
htsmsg_t *e = htsmsg_create_map();
linuxdvb_frontend_save((linuxdvb_frontend_t*)lh, e);
htsmsg_add_msg(l, idnode_uuid_as_str(&lh->mi_id), e);
htsmsg_add_msg(l, idnode_uuid_as_str(&lh->ti_id), e);
}
htsmsg_add_msg(m, "frontends", l);
}
@ -118,7 +118,7 @@ linuxdvb_adapter_create0
linuxdvb_adapter_t *la;
la = calloc(1, sizeof(linuxdvb_adapter_t));
if (idnode_insert(&la->mi_id, uuid, &linuxdvb_adapter_class)) {
if (idnode_insert(&la->ti_id, uuid, &linuxdvb_adapter_class)) {
free(la);
return NULL;
}
@ -131,7 +131,7 @@ linuxdvb_adapter_create0
if (!conf)
return la;
idnode_load(&la->mi_id, conf);
idnode_load(&la->ti_id, conf);
if (!htsmsg_get_u32(conf, "number", &u32))
la->la_number = u32;
if ((str = htsmsg_get_str(conf, "rootpath")))

View file

@ -175,20 +175,20 @@ void linuxdvb_device_save ( linuxdvb_device_t *ld )
m = htsmsg_create_map();
idnode_save(&ld->mi_id, m);
idnode_save(&ld->ti_id, m);
/* Adapters */
l = htsmsg_create_map();
LIST_FOREACH(lh, &ld->lh_children, lh_parent_link) {
e = htsmsg_create_map();
linuxdvb_adapter_save((linuxdvb_adapter_t*)lh, e);
htsmsg_add_msg(l, idnode_uuid_as_str(&lh->mi_id), e);
htsmsg_add_msg(l, idnode_uuid_as_str(&lh->ti_id), e);
}
htsmsg_add_msg(m, "adapters", l);
/* Save */
hts_settings_save(m, "input/linuxdvb/devices/%s",
idnode_uuid_as_str(&ld->mi_id));
idnode_uuid_as_str(&ld->ti_id));
}
const idclass_t linuxdvb_device_class =
@ -226,7 +226,7 @@ linuxdvb_device_create0 ( const char *uuid, htsmsg_t *conf )
/* Create */
ld = calloc(1, sizeof(linuxdvb_device_t));
if (idnode_insert(&ld->mi_id, uuid, &linuxdvb_device_class)) {
if (idnode_insert(&ld->ti_id, uuid, &linuxdvb_device_class)) {
free(ld);
return NULL;
}
@ -240,7 +240,7 @@ linuxdvb_device_create0 ( const char *uuid, htsmsg_t *conf )
return ld;
/* Load config */
idnode_load(&ld->mi_id, conf);
idnode_load(&ld->ti_id, conf);
get_min_dvb_adapter(&ld->ld_devid);
/* Adapters */

View file

@ -1,5 +1,4 @@
/*
rk_class
* Tvheadend - Linux DVB frontend
*
* Copyright (C) 2013 Adam Sutton
@ -461,42 +460,11 @@ linuxdvb_frontend_open_all
}
}
static void
linuxdvb_frontend_monitor_stats ( linuxdvb_frontend_t *lfe, const char *name )
{
int bw;
htsmsg_t *m, *l, *e;
mpegts_mux_instance_t *mmi;
/* Send message */
m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&lfe->mi_id));
htsmsg_add_str(m, "name", name);
htsmsg_add_str(m, "type", "linuxdvb");
/* Mux list */
if ((mmi = LIST_FIRST(&lfe->mi_mux_active))) {
char buf[256];
l = htsmsg_create_list();
e = htsmsg_create_map();
mmi->mmi_mux->mm_display_name(mmi->mmi_mux, buf, sizeof(buf));
htsmsg_add_str(e, "name", buf);
htsmsg_add_u32(e, "bytes", 0); // TODO
// TODO: signal info
htsmsg_add_msg(l, NULL, e);
htsmsg_add_msg(m, "muxes", l);
}
/* Total data */
bw = atomic_exchange(&lfe->mi_bytes, 0);
htsmsg_add_u32(m, "bytes", bw);
notify_by_msg("input", m);
}
static void
linuxdvb_frontend_monitor ( void *aux )
{
uint16_t u16;
uint32_t u32;
char buf[256];
linuxdvb_frontend_t *lfe = aux;
mpegts_mux_instance_t *mmi = LIST_FIRST(&lfe->mi_mux_active);
@ -573,8 +541,15 @@ linuxdvb_frontend_monitor ( void *aux )
}
}
/* Monitor stats */
linuxdvb_frontend_monitor_stats(lfe, buf);
/* Statistics */
if (!ioctl(lfe->lfe_fe_fd, FE_READ_SIGNAL_STRENGTH, &u16))
mmi->mmi_stats.signal = u16;
if (!ioctl(lfe->lfe_fe_fd, FE_READ_BER, &u32))
mmi->mmi_stats.ber = u32;
if (!ioctl(lfe->lfe_fe_fd, FE_READ_SNR, &u16))
mmi->mmi_stats.snr = u16;
if (!ioctl(lfe->lfe_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &u32))
mmi->mmi_stats.unc = u32;
}
static void *

View file

@ -35,7 +35,7 @@ linuxdvb_hardware_enumerate ( linuxdvb_hardware_list_t *list )
linuxdvb_hardware_t *lh;
idnode_set_t *set = idnode_set_create();
LIST_FOREACH(lh, list, lh_parent_link)
idnode_set_add(set, &lh->mi_id, NULL);
idnode_set_add(set, &lh->ti_id, NULL);
return set;
}

View file

@ -91,7 +91,7 @@ linuxdvb_satconf_class_frontend_get ( void *o )
static const char *s;
linuxdvb_satconf_t *ls = o;
if (ls->ls_frontend)
s = idnode_uuid_as_str(&ls->ls_frontend->mi_id);
s = idnode_uuid_as_str(&ls->ls_frontend->ti_id);
else
s = NULL;
return &s;
@ -123,7 +123,7 @@ linuxdvb_satconf_class_frontend_enum (void *o)
for (i = 0; i < is->is_count; i++) {
mpegts_input_t *mi = (mpegts_input_t*)is->is_array[i];
htsmsg_t *e = htsmsg_create_map();
htsmsg_add_str(e, "key", idnode_uuid_as_str(&mi->mi_id));
htsmsg_add_str(e, "key", idnode_uuid_as_str(&mi->ti_id));
*buf = 0;
mi->mi_display_name(mi, buf, sizeof(buf));
htsmsg_add_str(e, "val", buf);
@ -585,7 +585,7 @@ linuxdvb_diseqc_class_save ( idnode_t *o )
{
linuxdvb_diseqc_t *ld = (linuxdvb_diseqc_t*)o;
if (ld->ld_satconf)
linuxdvb_satconf_class_save(&ld->ld_satconf->mi_id);
linuxdvb_satconf_class_save(&ld->ld_satconf->ti_id);
}
const idclass_t linuxdvb_diseqc_class =

View file

@ -22,6 +22,7 @@
#include "streaming.h"
#include "subscriptions.h"
#include "atomic.h"
#include "notify.h"
#include <pthread.h>
#include <assert.h>
@ -164,8 +165,15 @@ static void
mpegts_input_started_mux
( mpegts_input_t *mi, mpegts_mux_instance_t *mmi )
{
/* Arm timer */
if (LIST_FIRST(&mi->mi_mux_active) == NULL)
gtimer_arm(&mi->mi_status_timer, mpegts_input_status_timer,
mi, 1);
/* Update */
mmi->mmi_mux->mm_active = mmi;
LIST_INSERT_HEAD(&mi->mi_mux_active, mmi, mmi_active_link);
notify_reload("input_status");
}
static void
@ -177,6 +185,10 @@ mpegts_input_stopped_mux
mmi->mmi_mux->mm_active = NULL;
LIST_REMOVE(mmi, mmi_active_link);
/* Disarm timer */
if (LIST_FIRST(&mi->mi_mux_active) == NULL)
gtimer_disarm(&mi->mi_status_timer);
mi->mi_display_name(mi, buf, sizeof(buf));
tvhtrace("mpegts", "%s - flush subscribers", buf);
s = LIST_FIRST(&mi->mi_transports);
@ -185,6 +197,7 @@ mpegts_input_stopped_mux
service_remove_subscriber(s, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN);
s = LIST_NEXT(s, s_active_link);
}
notify_reload("input_status");
}
static int
@ -290,7 +303,7 @@ mpegts_input_recv_packets
pthread_mutex_unlock(&mi->mi_delivery_mutex);
/* Bandwidth monitoring */
atomic_add(&mi->mi_bytes, i);
atomic_add(&mmi->mmi_stats.bps, i);
/* Reset buffer */
if (len) memmove(tsb, tsb+i, len);
@ -371,6 +384,59 @@ mpegts_input_flush_mux
pthread_mutex_unlock(&mi->mi_delivery_mutex);
}
static void
mpegts_input_stream_status
( mpegts_mux_instance_t *mmi, tvh_input_stream_t *st )
{
int s = 0, w = 0;
char buf[512];
mmi->mmi_mux->mm_display_name(mmi->mmi_mux, buf, sizeof(buf));
st->uuid = strdup(idnode_uuid_as_str(&mmi->mmi_id));
st->input_name = strdup(mmi->mmi_input->mi_displayname?:"");
st->stream_name = strdup(buf);
st->subs_count = s;
st->max_weight = w;
st->stats = mmi->mmi_stats;
st->stats.bps = atomic_exchange(&mmi->mmi_stats.bps, 0) * 8;
}
static void
mpegts_input_get_streams
( tvh_input_t *i, tvh_input_stream_list_t *isl )
{
tvh_input_stream_t *st;
mpegts_input_t *mi = (mpegts_input_t*)i;
mpegts_mux_instance_t *mmi;
LIST_FOREACH(mmi, &mi->mi_mux_active, mmi_active_link) {
st = calloc(1, sizeof(tvh_input_stream_t));
mpegts_input_stream_status(mmi, st);
LIST_INSERT_HEAD(isl, st, link);
}
}
/* **************************************************************************
* Status monitoring
* *************************************************************************/
void
mpegts_input_status_timer ( void *p )
{
tvh_input_stream_t st;
mpegts_input_t *mi = p;
mpegts_mux_instance_t *mmi;
htsmsg_t *e;
LIST_FOREACH(mmi, &mi->mi_mux_active, mmi_active_link) {
memset(&st, 0, sizeof(st));
mpegts_input_stream_status(mmi, &st);
e = tvh_input_stream_create_msg(&st);
htsmsg_add_u32(e, "update", 1);
notify_by_msg("input_status", e);
tvh_input_stream_destroy(&st);
}
gtimer_arm(&mi->mi_status_timer, mpegts_input_status_timer, mi, 1);
}
/* **************************************************************************
* Creation/Config
* *************************************************************************/
@ -383,7 +449,8 @@ mpegts_input_create0
( mpegts_input_t *mi, const idclass_t *class, const char *uuid,
htsmsg_t *c )
{
idnode_insert(&mi->mi_id, uuid, class);
idnode_insert(&mi->ti_id, uuid, class);
LIST_INSERT_HEAD(&tvh_inputs, (tvh_input_t*)mi, ti_link);
/* Defaults */
mi->mi_is_enabled = mpegts_input_is_enabled;
@ -398,6 +465,7 @@ mpegts_input_create0
mi->mi_started_mux = mpegts_input_started_mux;
mi->mi_stopped_mux = mpegts_input_stopped_mux;
mi->mi_has_subscription = mpegts_input_has_subscription;
mi->ti_get_streams = mpegts_input_get_streams;
/* Index */
mi->mi_instance = ++mpegts_input_idx;
@ -417,7 +485,7 @@ mpegts_input_create0
/* Load config */
if (c)
idnode_load(&mi->mi_id, c);
idnode_load(&mi->ti_id, c);
return mi;
}
@ -425,7 +493,7 @@ mpegts_input_create0
void
mpegts_input_save ( mpegts_input_t *mi, htsmsg_t *m )
{
idnode_save(&mi->mi_id, m);
idnode_save(&mi->ti_id, m);
}
void

View file

@ -136,9 +136,61 @@ tvheadend.status_subs = function() {
/**
*
* Streams
*/
tvheadend.status_adapters = function() {
tvheadend.status_streams = function() {
var stream_store = new Ext.data.JsonStore({
root : 'entries',
totalProperty : 'totalCount',
fields : [ {
name : 'uuid'
}, {
name : 'input'
}, {
name : 'username'
}, {
name : 'stream'
}, {
name : 'subs'
}, {
name : 'weight'
}, {
name : 'signal'
}, {
name : 'ber'
}, {
name : 'unc'
}, {
name : 'snr'
}, {
name : 'bps'
},
],
url : 'api/input/status',
autoLoad : true,
id : 'uuid'
});
tvheadend.comet.on('input_status', function(m){
if (m.reload != null) stream_store.reload();
if (m.update != null) {
var r = stream_store.getById(m.uuid);
if (r) {
r.data.subs = m.subs;
r.data.weight = m.weight;
r.data.signal = m.signal;
r.data.ber = m.ber;
r.data.unc = m.unc;
r.data.snr = m.snr;
r.data.bps = m.bps;
stream_store.afterEdit(r);
stream_store.fireEvent('updated', stream_store, r, Ext.data.Record.COMMIT);
} else {
stream_store.reload();
}
}
});
var signal = new Ext.ux.grid.ProgressColumn({
header : "Signal Strength",
@ -149,30 +201,30 @@ tvheadend.status_adapters = function() {
});
function renderBw(value) {
return parseInt(value / 125);
return parseInt(value / 1024);
}
var cm = new Ext.grid.ColumnModel([{
width : 50,
header : "Name",
dataIndex : 'name'
},{
width : 50,
header : "Hardware device",
dataIndex : 'path'
width : 100,
header : "Input",
dataIndex : 'input'
},{
width : 100,
header : "Currently tuned to",
dataIndex : 'currentMux'
header : "Stream",
dataIndex : 'stream'
},{
width : 50,
header : "Subs #",
dataIndex : 'subs'
},{
width : 50,
header : "Weight",
dataIndex : 'weight'
},{
width : 100,
header : "Bandwidth (kb/s)",
dataIndex : 'bw',
dataIndex : 'bps',
renderer: renderBw
},{
width : 50,
header : "Used for",
dataIndex : 'reason'
},{
width : 50,
header : "Bit error rate",
@ -180,7 +232,7 @@ tvheadend.status_adapters = function() {
},{
width : 50,
header : "Uncorrected bit error rate",
dataIndex : 'uncavg'
dataIndex : 'unc'
},{
width : 50,
header : "SNR",
@ -199,9 +251,9 @@ tvheadend.status_adapters = function() {
loadMask : true,
stripeRows : true,
disableSelection : true,
title : 'Adapters',
title : 'Stream',
iconCls : 'hardware',
store : tvheadend.tvAdapterStore,
store : stream_store,
cm : cm,
flex: 1,
viewConfig : {
@ -218,7 +270,7 @@ tvheadend.status = function() {
layout : 'vbox',
title : 'Status',
iconCls : 'eye',
items : [ new tvheadend.status_subs, new tvheadend.status_adapters ]
items : [ new tvheadend.status_subs, new tvheadend.status_streams ]
});
return panel;