tvheadend/src/input/mpegts/mpegts_service.c

493 lines
12 KiB
C

/*
* MPEGTS (DVB) based service
*
* Copyright (C) 2013 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/>.
*/
#include <assert.h>
#include "service.h"
#include "input.h"
#include "settings.h"
#include "dvb_charset.h"
/* **************************************************************************
* Class definition
* *************************************************************************/
extern const idclass_t service_class;
static const void *
mpegts_service_class_get_mux ( void *ptr )
{
static char buf[512], *s = buf;
mpegts_service_t *ms = ptr;
if (ms->s_dvb_mux && ms->s_dvb_mux->mm_display_name)
ms->s_dvb_mux->mm_display_name(ms->s_dvb_mux, buf, sizeof(buf));
else
*buf = 0;
return &s;
}
static const void *
mpegts_service_class_get_network ( void *ptr )
{
static char buf[512], *s = buf;
mpegts_service_t *ms = ptr;
mpegts_network_t *mn = ms->s_dvb_mux ? ms->s_dvb_mux->mm_network : NULL;
if (mn && mn->mn_display_name)
mn->mn_display_name(mn, buf, sizeof(buf));
else
*buf = 0;
return &s;
}
static htsmsg_t *
mpegts_service_pref_capid_lock_list ( void *o )
{
static const struct strtab tab[] = {
{ "Off", 0 },
{ "On", 1 },
{ "Only Pref. CA PID", 2 },
};
return strtab2htsmsg(tab);
}
const idclass_t mpegts_service_class =
{
.ic_super = &service_class,
.ic_class = "mpegts_service",
.ic_caption = "MPEGTS Service",
.ic_order = "enabled,channel,svcname",
.ic_properties = (const property_t[]){
{
.type = PT_STR,
.id = "network",
.name = "Network",
.opts = PO_RDONLY | PO_NOSAVE,
.get = mpegts_service_class_get_network,
},
{
.type = PT_STR,
.id = "multiplex",
.name = "Mux",
.opts = PO_RDONLY | PO_NOSAVE,
.get = mpegts_service_class_get_mux,
},
{
.type = PT_U16,
.id = "sid",
.name = "Service ID",
.opts = PO_RDONLY,
.off = offsetof(mpegts_service_t, s_dvb_service_id),
},
{
.type = PT_U16,
.id = "lcn",
.name = "Local Channel Number",
.opts = PO_RDONLY,
.off = offsetof(mpegts_service_t, s_dvb_channel_num),
},
{
.type = PT_STR,
.id = "svcname",
.name = "Service Name",
.opts = PO_RDONLY,
.off = offsetof(mpegts_service_t, s_dvb_svcname),
},
{
.type = PT_STR,
.id = "provider",
.name = "Provider",
.opts = PO_RDONLY | PO_HIDDEN,
.off = offsetof(mpegts_service_t, s_dvb_provider),
},
{
.type = PT_STR,
.id = "cridauth",
.name = "CRID Authority",
.opts = PO_RDONLY | PO_HIDDEN,
.off = offsetof(mpegts_service_t, s_dvb_cridauth),
},
{
.type = PT_U16,
.id = "dvb_servicetype",
.name = "Service Type",
.opts = PO_RDONLY | PO_HIDDEN,
.off = offsetof(mpegts_service_t, s_dvb_servicetype),
},
{
.type = PT_STR,
.id = "charset",
.name = "Character Set",
.off = offsetof(mpegts_service_t, s_dvb_charset),
.list = dvb_charset_enum,
.opts = PO_ADVANCED,
},
{
.type = PT_U16,
.id = "prefcapid",
.name = "Pref. CA PID",
.off = offsetof(mpegts_service_t, s_dvb_prefcapid),
.opts = PO_ADVANCED,
},
{
.type = PT_INT,
.id = "prefcapid_lock",
.name = "Lock Pref. CA PID",
.off = offsetof(mpegts_service_t, s_dvb_prefcapid_lock),
.opts = PO_ADVANCED,
.list = mpegts_service_pref_capid_lock_list,
},
{},
}
};
/* **************************************************************************
* Class methods
* *************************************************************************/
/*
* Check the service is enabled
*/
static int
mpegts_service_is_enabled(service_t *t, int flags)
{
mpegts_service_t *s = (mpegts_service_t*)t;
mpegts_mux_t *mm = s->s_dvb_mux;
return mm->mm_is_enabled(mm) ? s->s_enabled : 0;
}
/*
* Save
*/
static void
mpegts_service_config_save ( service_t *t )
{
htsmsg_t *c = htsmsg_create_map();
mpegts_service_t *s = (mpegts_service_t*)t;
service_save(t, c);
hts_settings_save(c, "input/dvb/networks/%s/muxes/%s/services/%s",
idnode_uuid_as_str(&s->s_dvb_mux->mm_network->mn_id),
idnode_uuid_as_str(&s->s_dvb_mux->mm_id),
idnode_uuid_as_str(&s->s_id));
htsmsg_destroy(c);
}
/*
* Service instance list
*/
static void
mpegts_service_enlist(service_t *t, struct service_instance_list *sil, int flags)
{
int p = 0, w;
mpegts_service_t *s = (mpegts_service_t*)t;
mpegts_mux_t *m = s->s_dvb_mux;
mpegts_mux_instance_t *mmi;
assert(s->s_source_type == S_MPEG_TS);
/* Create instances */
m->mm_create_instances(m);
/* Enlist available */
LIST_FOREACH(mmi, &m->mm_instances, mmi_mux_link) {
if (mmi->mmi_tune_failed)
continue;
if (!mmi->mmi_input->mi_is_enabled(mmi->mmi_input, mmi->mmi_mux, "service")) continue;
/* Set weight to -1 (forced) for already active mux */
if (mmi->mmi_mux->mm_active == mmi) {
w = -1;
p = -1;
} else {
w = mmi->mmi_input->mi_get_weight(mmi->mmi_input, flags);
p = mmi->mmi_input->mi_get_priority(mmi->mmi_input, mmi->mmi_mux, flags);
}
service_instance_add(sil, t, mmi->mmi_input->mi_instance, p, w);
}
}
/*
* Start service
*/
static int
mpegts_service_start(service_t *t, int instance)
{
int r;
mpegts_service_t *s = (mpegts_service_t*)t;
mpegts_mux_t *m = s->s_dvb_mux;
mpegts_mux_instance_t *mmi;
/* Validate */
assert(s->s_status == SERVICE_IDLE);
assert(s->s_source_type == S_MPEG_TS);
lock_assert(&global_lock);
/* Find */
LIST_FOREACH(mmi, &m->mm_instances, mmi_mux_link)
if (mmi->mmi_input->mi_instance == instance)
break;
assert(mmi != NULL);
if (mmi == NULL)
return SM_CODE_UNDEFINED_ERROR;
/* Start Mux */
r = mpegts_mux_instance_start(&mmi);
/* Start */
if (!r) {
/* Open service */
mmi->mmi_input->mi_open_service(mmi->mmi_input, s, 1);
}
return r;
}
/*
* Stop service
*/
static void
mpegts_service_stop(service_t *t)
{
mpegts_service_t *s = (mpegts_service_t*)t;
mpegts_input_t *i = s->s_dvb_active_input;
/* Validate */
assert(s->s_source_type == S_MPEG_TS);
lock_assert(&global_lock);
/* Stop */
if (i)
i->mi_close_service(i, s);
/* Save some memory */
sbuf_free(&s->s_tsbuf);
}
/*
* Refresh
*/
static void
mpegts_service_refresh(service_t *t)
{
mpegts_service_t *s = (mpegts_service_t*)t;
mpegts_input_t *i = s->s_dvb_active_input;
/* Validate */
assert(s->s_source_type == S_MPEG_TS);
assert(i != NULL);
lock_assert(&global_lock);
/* Re-open */
i->mi_open_service(i, s, 0);
}
/*
* Source info
*/
static void
mpegts_service_setsourceinfo(service_t *t, source_info_t *si)
{
char buf[256];
mpegts_service_t *s = (mpegts_service_t*)t;
mpegts_mux_t *m = s->s_dvb_mux;
/* Validate */
assert(s->s_source_type == S_MPEG_TS);
lock_assert(&global_lock);
/* Update */
memset(si, 0, sizeof(struct source_info));
si->si_type = S_MPEG_TS;
if(m->mm_network->mn_network_name != NULL)
si->si_network = strdup(m->mm_network->mn_network_name);
m->mm_display_name(m, buf, sizeof(buf));
si->si_mux = strdup(buf);
if(s->s_dvb_active_input) {
mpegts_input_t *mi = s->s_dvb_active_input;
mi->mi_display_name(mi, buf, sizeof(buf));
si->si_adapter = strdup(buf);
}
if(s->s_dvb_provider != NULL)
si->si_provider = strdup(s->s_dvb_provider);
if(s->s_dvb_svcname != NULL)
si->si_service = strdup(s->s_dvb_svcname);
}
/*
* Grace period
*/
static int
mpegts_service_grace_period(service_t *t)
{
int r = 0;
mpegts_service_t *ms = (mpegts_service_t*)t;
mpegts_mux_t *mm = ms->s_dvb_mux;
mpegts_input_t *mi = ms->s_dvb_active_input;
assert(mi != NULL);
if (mi && mi->mi_get_grace)
r = mi->mi_get_grace(mi, mm);
return r ?: 10;
}
/*
* Channel number
*/
static int
mpegts_service_channel_number ( service_t *s )
{
return ((mpegts_service_t*)s)->s_dvb_channel_num;
}
static const char *
mpegts_service_channel_name ( service_t *s )
{
return ((mpegts_service_t*)s)->s_dvb_svcname;
}
static const char *
mpegts_service_provider_name ( service_t *s )
{
return ((mpegts_service_t*)s)->s_dvb_provider;
}
void
mpegts_service_delete ( service_t *t, int delconf )
{
mpegts_service_t *ms = (mpegts_service_t*)t;
mpegts_mux_t *mm = ms->s_dvb_mux;
/* Remove config */
if (delconf)
hts_settings_remove("input/dvb/networks/%s/muxes/%s/services/%s",
idnode_uuid_as_str(&mm->mm_network->mn_id),
idnode_uuid_as_str(&mm->mm_id),
idnode_uuid_as_str(&t->s_id));
/* Free memory */
free(ms->s_dvb_svcname);
free(ms->s_dvb_provider);
free(ms->s_dvb_charset);
LIST_REMOVE(ms, s_dvb_mux_link);
sbuf_free(&ms->s_tsbuf);
// Note: the ultimate deletion and removal from the idnode list
// is done in service_destroy
}
/* **************************************************************************
* Creation/Location
* *************************************************************************/
/*
* Create service
*/
mpegts_service_t *
mpegts_service_create0
( mpegts_service_t *s, const idclass_t *class, const char *uuid,
mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid, htsmsg_t *conf )
{
int r;
char buf[256];
service_create0((service_t*)s, class, uuid, S_MPEG_TS, conf);
/* Create */
sbuf_init(&s->s_tsbuf);
if (!conf) {
if (sid) s->s_dvb_service_id = sid;
if (pmt_pid) s->s_pmt_pid = pmt_pid;
}
s->s_dvb_mux = mm;
if ((r = dvb_servicetype_lookup(s->s_dvb_servicetype)) != -1)
s->s_servicetype = r;
LIST_INSERT_HEAD(&mm->mm_services, s, s_dvb_mux_link);
s->s_delete = mpegts_service_delete;
s->s_is_enabled = mpegts_service_is_enabled;
s->s_config_save = mpegts_service_config_save;
s->s_enlist = mpegts_service_enlist;
s->s_start_feed = mpegts_service_start;
s->s_stop_feed = mpegts_service_stop;
s->s_refresh_feed = mpegts_service_refresh;
s->s_setsourceinfo = mpegts_service_setsourceinfo;
s->s_grace_period = mpegts_service_grace_period;
s->s_channel_number = mpegts_service_channel_number;
s->s_channel_name = mpegts_service_channel_name;
s->s_provider_name = mpegts_service_provider_name;
pthread_mutex_lock(&s->s_stream_mutex);
service_make_nicename((service_t*)s);
pthread_mutex_unlock(&s->s_stream_mutex);
mpegts_mux_nice_name(mm, buf, sizeof(buf));
tvhlog(LOG_DEBUG, "mpegts", "%s - add service %04X %s", buf, s->s_dvb_service_id, s->s_dvb_svcname);
/* Notification */
idnode_updated(&mm->mm_id);
idnode_updated(&mm->mm_network->mn_id);
return s;
}
/*
* Find service
*/
mpegts_service_t *
mpegts_service_find
( mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid,
int create, int *save )
{
mpegts_service_t *s;
/* Validate */
lock_assert(&global_lock);
/* Find existing service */
LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link) {
if (s->s_dvb_service_id == sid) {
if (pmt_pid && pmt_pid != s->s_pmt_pid) {
s->s_pmt_pid = pmt_pid;
if (save) *save = 1;
}
return s;
}
}
/* Create */
if (create) {
s = mm->mm_network->mn_create_service(mm, sid, pmt_pid);
if (save) *save = 1;
}
return s;
}
/******************************************************************************
* Editor Configuration
*
* vim:sts=2:ts=2:sw=2:et
*****************************************************************************/