diff --git a/Makefile b/Makefile
index 04ba2840..830f62c2 100644
--- a/Makefile
+++ b/Makefile
@@ -122,7 +122,7 @@ SRCS = src/version.c \
src/trap.c \
src/avg.c \
src/htsstr.c \
- src/tvhpoll.c \
+ src/tvhpoll.c \
src/huffman.c \
src/filebundle.c \
src/config.c \
@@ -137,7 +137,8 @@ SRCS = src/version.c \
src/fsmonitor.c \
src/cron.c \
src/esfilter.c \
- src/intlconv.c
+ src/intlconv.c \
+ src/profile.c
SRCS-${CONFIG_UPNP} += \
src/upnp.c
@@ -157,7 +158,8 @@ SRCS += \
src/api/api_intlconv.c \
src/api/api_access.c \
src/api/api_dvr.c \
- src/api/api_caclient.c
+ src/api/api_caclient.c \
+ src/api/api_profile.c
SRCS += \
src/parsers/parsers.c \
diff --git a/src/api.c b/src/api.c
index f0ac5be3..8594706a 100644
--- a/src/api.c
+++ b/src/api.c
@@ -134,6 +134,7 @@ void api_init ( void )
api_access_init();
api_dvr_init();
api_caclient_init();
+ api_profile_init();
}
void api_done ( void )
diff --git a/src/api.h b/src/api.h
index c6fe371c..4928484d 100644
--- a/src/api.h
+++ b/src/api.h
@@ -74,6 +74,7 @@ void api_intlconv_init ( void );
void api_access_init ( void );
void api_dvr_init ( void );
void api_caclient_init ( void );
+void api_profile_init ( void );
/*
* IDnode
diff --git a/src/api/api_caclient.c b/src/api/api_caclient.c
index dc30cc85..505bce19 100644
--- a/src/api/api_caclient.c
+++ b/src/api/api_caclient.c
@@ -34,6 +34,7 @@ api_caclient_list
htsmsg_t *l, *e;
l = htsmsg_create_list();
+ pthread_mutex_lock(&global_lock);
TAILQ_FOREACH(cac, &caclients, cac_link) {
e = htsmsg_create_map();
htsmsg_add_str(e, "uuid", idnode_uuid_as_str(&cac->cac_id));
@@ -41,6 +42,7 @@ api_caclient_list
htsmsg_add_str(e, "status", caclient_get_status(cac));
htsmsg_add_msg(l, NULL, e);
}
+ pthread_mutex_unlock(&global_lock);
*resp = htsmsg_create_map();
htsmsg_add_msg(*resp, "entries", l);
return 0;
diff --git a/src/api/api_profile.c b/src/api/api_profile.c
new file mode 100644
index 00000000..7d9ed663
--- /dev/null
+++ b/src/api/api_profile.c
@@ -0,0 +1,110 @@
+/*
+ * tvheadend - API access to Stream Profile
+ *
+ * Copyright (C) 2014 Jaroslav Kysela
+ *
+ * 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 .
+ */
+
+#include "tvheadend.h"
+#include "access.h"
+#include "htsmsg.h"
+#include "api.h"
+#include "profile.h"
+
+/*
+ *
+ */
+static int
+api_profile_list
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ profile_t *pro;
+ htsmsg_t *l, *e;
+
+ l = htsmsg_create_list();
+ pthread_mutex_lock(&global_lock);
+ TAILQ_FOREACH(pro, &profiles, pro_link) {
+ e = htsmsg_create_map();
+ htsmsg_add_str(e, "key", idnode_uuid_as_str(&pro->pro_id));
+ htsmsg_add_str(e, "val", profile_get_name(pro));
+ htsmsg_add_msg(l, NULL, e);
+ }
+ pthread_mutex_unlock(&global_lock);
+ *resp = htsmsg_create_map();
+ htsmsg_add_msg(*resp, "entries", l);
+ return 0;
+}
+
+static int
+api_profile_builders
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ profile_build_t *pb;
+ htsmsg_t *l, *e;
+
+ l = htsmsg_create_list();
+
+ pthread_mutex_lock(&global_lock);
+ /* List of available builder classes */
+ LIST_FOREACH(pb, &profile_builders, link)
+ if ((e = idclass_serialize(pb->clazz)))
+ htsmsg_add_msg(l, NULL, e);
+ pthread_mutex_unlock(&global_lock);
+
+ /* Output */
+ *resp = htsmsg_create_map();
+ htsmsg_add_msg(*resp, "entries", l);
+
+ return 0;
+}
+
+static int
+api_profile_create
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ int err = 0;
+ const char *clazz;
+ htsmsg_t *conf;
+
+ if (!(clazz = htsmsg_get_str(args, "class")))
+ return EINVAL;
+ if (!(conf = htsmsg_get_map(args, "conf")))
+ return EINVAL;
+ htsmsg_set_str(conf, "class", clazz);
+
+ pthread_mutex_lock(&global_lock);
+ if (profile_create(NULL, conf, 1) == NULL)
+ err = -EINVAL;
+ pthread_mutex_unlock(&global_lock);
+
+ return err;
+}
+
+/*
+ * Init
+ */
+void
+api_profile_init ( void )
+{
+ static api_hook_t ah[] = {
+ { "profile/list", ACCESS_ADMIN, api_profile_list, NULL },
+ { "profile/class", ACCESS_ADMIN, api_idnode_class, (void*)&profile_class },
+ { "profile/builders", ACCESS_ADMIN, api_profile_builders, NULL },
+ { "profile/create", ACCESS_ADMIN, api_profile_create, NULL },
+ { NULL },
+ };
+
+ api_register_all(ah);
+}
diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h
index eccace43..1dce3c65 100644
--- a/src/dvr/dvr.h
+++ b/src/dvr/dvr.h
@@ -24,16 +24,19 @@
#include "channels.h"
#include "subscriptions.h"
#include "muxer.h"
+#include "profile.h"
#include "lang_str.h"
typedef struct dvr_config {
idnode_t dvr_id;
LIST_ENTRY(dvr_config) config_link;
+ LIST_ENTRY(dvr_config) profile_link;
int dvr_enabled;
int dvr_valid;
char *dvr_config_name;
+ profile_t *dvr_profile;
char *dvr_storage;
uint32_t dvr_retention_days;
char *dvr_charset;
@@ -43,7 +46,6 @@ typedef struct dvr_config {
uint32_t dvr_extra_time_post;
uint32_t dvr_update_window;
- int dvr_mc;
muxer_config_t dvr_muxcnf;
int dvr_dir_per_day;
@@ -208,15 +210,11 @@ typedef struct dvr_entry {
pthread_t de_thread;
th_subscription_t *de_s;
- streaming_queue_t de_sq;
- streaming_target_t *de_tsfix;
- streaming_target_t *de_gh;
-
- /**
- * Initialized upon SUBSCRIPTION_TRANSPORT_RUN
- */
- struct muxer *de_mux;
+ /**
+ * Stream worker chain
+ */
+ profile_chain_t *de_chain;
/**
* Inotify
@@ -350,6 +348,8 @@ void dvr_config_delete(const char *name);
void dvr_config_save(dvr_config_t *cfg);
+void dvr_config_destroy_by_profile(profile_t *pro, int delconf);
+
/*
*
*/
diff --git a/src/dvr/dvr_config.c b/src/dvr/dvr_config.c
index 1175dcec..6edf256e 100644
--- a/src/dvr/dvr_config.c
+++ b/src/dvr/dvr_config.c
@@ -139,7 +139,6 @@ dvr_config_create(const char *name, const char *uuid, htsmsg_t *conf)
cfg->dvr_enabled = 1;
cfg->dvr_config_name = strdup(name);
cfg->dvr_retention_days = 31;
- cfg->dvr_mc = MC_MATROSKA;
cfg->dvr_tag_files = 1;
cfg->dvr_skip_commercials = 1;
dvr_charset_update(cfg, intlconv_filesystem_charset());
@@ -155,7 +154,6 @@ dvr_config_create(const char *name, const char *uuid, htsmsg_t *conf)
/* Muxer config */
cfg->dvr_muxcnf.m_cache = MC_CACHE_DONTKEEP;
- cfg->dvr_muxcnf.m_rewrite_pat = 1;
/* dup detect */
cfg->dvr_dup_detect_episode = 1; // detect dup episodes
@@ -172,6 +170,12 @@ dvr_config_create(const char *name, const char *uuid, htsmsg_t *conf)
tvhinfo("dvr", "Creating new configuration '%s'", cfg->dvr_config_name);
+ if (cfg->dvr_profile == NULL) {
+ cfg->dvr_profile = profile_find_by_name(NULL);
+ assert(cfg->dvr_profile);
+ LIST_INSERT_HEAD(&cfg->dvr_profile->pro_dvr_configs, cfg, profile_link);
+ }
+
if (dvr_config_is_default(cfg) && dvr_config_find_by_name(NULL)) {
tvherror("dvr", "Unable to create second default config, removing");
LIST_INSERT_HEAD(&dvrconfigs, cfg, config_link);
@@ -201,6 +205,11 @@ dvr_config_destroy(dvr_config_t *cfg, int delconf)
LIST_REMOVE(cfg, config_link);
idnode_unlink(&cfg->dvr_id);
+ if (cfg->dvr_profile) {
+ LIST_REMOVE(cfg, profile_link);
+ cfg->dvr_profile = NULL;
+ }
+
dvr_entry_destroy_by_config(cfg, delconf);
access_destroy_by_dvr_config(cfg, delconf);
autorec_destroy_by_config(cfg, delconf);
@@ -327,6 +336,51 @@ dvr_config_class_name_set(void *o, const void *v)
return 0;
}
+static int
+dvr_config_class_profile_set(void *o, const void *v)
+{
+ dvr_config_t *cfg = (dvr_config_t *)o;
+ profile_t *pro;
+
+ pro = v ? profile_find_by_uuid(v) : NULL;
+ pro = pro ?: profile_find_by_name(v);
+ if (pro == NULL) {
+ if (cfg->dvr_profile) {
+ LIST_REMOVE(cfg, profile_link);
+ cfg->dvr_profile = NULL;
+ return 1;
+ }
+ } else if (cfg->dvr_profile != pro) {
+ if (cfg->dvr_profile)
+ LIST_REMOVE(cfg, profile_link);
+ cfg->dvr_profile = pro;
+ LIST_INSERT_HEAD(&pro->pro_dvr_configs, cfg, profile_link);
+ return 1;
+ }
+ return 0;
+}
+
+static const void *
+dvr_config_class_profile_get(void *o)
+{
+ static const char *ret;
+ dvr_config_t *cfg = (dvr_config_t *)o;
+ if (cfg->dvr_profile)
+ ret = idnode_uuid_as_str(&cfg->dvr_profile->pro_id);
+ else
+ ret = "";
+ return &ret;
+}
+
+static char *
+dvr_config_class_profile_rend(void *o)
+{
+ dvr_config_t *cfg = (dvr_config_t *)o;
+ if (cfg->dvr_profile)
+ return strdup(profile_get_name(cfg->dvr_profile));
+ return NULL;
+}
+
static const char *
dvr_config_class_get_title (idnode_t *self)
{
@@ -433,12 +487,14 @@ const idclass_t dvr_config_class = {
.get_opts = dvr_config_class_enabled_opts,
},
{
- .type = PT_INT,
- .id = "container",
- .name = "Container",
- .off = offsetof(dvr_config_t, dvr_mc),
- .def.i = MC_MATROSKA,
- .list = dvr_entry_class_mc_list,
+ .type = PT_STR,
+ .id = "profile",
+ .name = "Stream Profile",
+ .off = offsetof(dvr_config_t, dvr_profile),
+ .set = dvr_config_class_profile_set,
+ .get = dvr_config_class_profile_get,
+ .rend = dvr_config_class_profile_rend,
+ .list = profile_class_get_list,
.group = 1,
},
{
@@ -522,21 +578,6 @@ const idclass_t dvr_config_class = {
.def.s = "UTF-8",
.group = 2,
},
- {
- .type = PT_BOOL,
- .id = "rewrite-pat",
- .name = "Rewrite PAT",
- .off = offsetof(dvr_config_t, dvr_muxcnf.m_rewrite_pat),
- .def.i = 1,
- .group = 2,
- },
- {
- .type = PT_BOOL,
- .id = "rewrite-pmt",
- .name = "Rewrite PMT",
- .off = offsetof(dvr_config_t, dvr_muxcnf.m_rewrite_pmt),
- .group = 2,
- },
{
.type = PT_BOOL,
.id = "tag-files",
@@ -649,6 +690,20 @@ const idclass_t dvr_config_class = {
},
};
+/**
+ *
+ */
+void
+dvr_config_destroy_by_profile(profile_t *pro, int delconf)
+{
+ dvr_config_t *cfg;
+
+ while((cfg = LIST_FIRST(&pro->pro_dvr_configs)) != NULL) {
+ LIST_REMOVE(cfg, profile_link);
+ cfg->dvr_profile = profile_find_by_name(NULL);
+ }
+}
+
/**
*
*/
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index 2764c50c..c31144f7 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -103,7 +103,7 @@ dvr_entry_get_mc( dvr_entry_t *de )
{
if (de->de_mc >= 0)
return de->de_mc;
- return de->de_config->dvr_mc;
+ return profile_get_mc(de->de_config->dvr_profile);
}
int
@@ -1779,7 +1779,7 @@ const idclass_t dvr_entry_class = {
.def.i = MC_MATROSKA,
.set = dvr_entry_class_mc_set,
.list = dvr_entry_class_mc_list,
- .opts = PO_SORTKEY
+ .opts = PO_RDONLY
},
{
.type = PT_STR,
diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c
index a1113caa..3b8b914f 100644
--- a/src/dvr/dvr_rec.c
+++ b/src/dvr/dvr_rec.c
@@ -63,10 +63,11 @@ dvr_rec_subscribe(dvr_entry_t *de)
{
char buf[100];
int weight;
- streaming_target_t *st;
- int flags;
+ profile_t *pro;
+ profile_chain_t *prch;
assert(de->de_s == NULL);
+ assert(de->de_chain == NULL);
if(de->de_pri < ARRAY_SIZE(prio2weight))
weight = prio2weight[de->de_pri];
@@ -75,23 +76,28 @@ dvr_rec_subscribe(dvr_entry_t *de)
snprintf(buf, sizeof(buf), "DVR: %s", lang_str_get(de->de_title, NULL));
- if(dvr_entry_get_mc(de) == MC_PASS) {
- streaming_queue_init(&de->de_sq, SMT_PACKET);
- de->de_gh = NULL;
- de->de_tsfix = NULL;
- st = &de->de_sq.sq_st;
- flags = SUBSCRIPTION_RAW_MPEGTS;
- } else {
- streaming_queue_init(&de->de_sq, 0);
- de->de_gh = globalheaders_create(&de->de_sq.sq_st);
- st = de->de_tsfix = tsfix_create(de->de_gh);
- tsfix_set_start_time(de->de_tsfix, dvr_entry_get_start_time(de));
- flags = 0;
+ pro = de->de_config->dvr_profile;
+ prch = malloc(sizeof(*prch));
+ if (pro->pro_open(pro, prch, &de->de_config->dvr_muxcnf, 0)) {
+ tvherror("dvr", "unable to create new channel streaming chain for '%s'",
+ channel_get_name(de->de_channel));
+ return;
}
de->de_s = subscription_create_from_channel(de->de_channel, weight,
- buf, st, flags,
+ buf, prch->prch_st,
+ prch->prch_flags,
NULL, NULL, NULL);
+ if (de->de_s == NULL) {
+ tvherror("dvr", "unable to create new channel subcription for '%s'",
+ channel_get_name(de->de_channel));
+ profile_chain_close(prch);
+ free(prch);
+ de->de_chain = NULL;
+ return;
+ }
+
+ de->de_chain = prch;
tvhthread_create(&de->de_thread, NULL, dvr_thread, de);
}
@@ -102,20 +108,21 @@ dvr_rec_subscribe(dvr_entry_t *de)
void
dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode)
{
- assert(de->de_s != NULL);
+ profile_chain_t *prch = de->de_chain;
- streaming_target_deliver(&de->de_sq.sq_st, streaming_msg_create(SMT_EXIT));
+ assert(de->de_s != NULL);
+ assert(prch != NULL);
+
+ streaming_target_deliver(prch->prch_st, streaming_msg_create(SMT_EXIT));
pthread_join(de->de_thread, NULL);
subscription_unsubscribe(de->de_s);
de->de_s = NULL;
- if(de->de_tsfix)
- tsfix_destroy(de->de_tsfix);
-
- if(de->de_gh)
- globalheaders_destroy(de->de_gh);
+ de->de_chain = NULL;
+ profile_chain_close(prch);
+ free(prch);
de->de_last_error = stopcode;
}
@@ -235,7 +242,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
if (filename == NULL)
return -1;
snprintf(fullname, sizeof(fullname), "%s/%s.%s",
- path, filename, muxer_suffix(de->de_mux, ss));
+ path, filename, muxer_suffix(de->de_chain->prch_muxer, ss));
while(1) {
if(stat(fullname, &st) == -1) {
@@ -250,7 +257,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
tally++;
snprintf(fullname, sizeof(fullname), "%s/%s-%d.%s",
- path, filename, tally, muxer_suffix(de->de_mux, ss));
+ path, filename, tally, muxer_suffix(de->de_chain->prch_muxer, ss));
}
free(filename);
@@ -310,17 +317,23 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
const streaming_start_component_t *ssc;
int i;
dvr_config_t *cfg = de->de_config;
- muxer_container_type_t mc;
+ profile_chain_t *prch = de->de_chain;
+ muxer_t *muxer;
if (!cfg) {
dvr_rec_fatal_error(de, "Unable to determine config profile");
return -1;
}
- mc = dvr_entry_get_mc(de);
+ if (!prch) {
+ dvr_rec_fatal_error(de, "Unable to determine stream profile");
+ return -1;
+ }
- de->de_mux = muxer_create(mc, &cfg->dvr_muxcnf);
- if(!de->de_mux) {
+ if (!(muxer = prch->prch_muxer))
+ muxer = prch->prch_muxer = muxer_create(&cfg->dvr_muxcnf);
+
+ if(!muxer) {
dvr_rec_fatal_error(de, "Unable to create muxer");
return -1;
}
@@ -330,18 +343,18 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
return -1;
}
- if(muxer_open_file(de->de_mux, de->de_filename)) {
+ if(muxer_open_file(muxer, de->de_filename)) {
dvr_rec_fatal_error(de, "Unable to open file");
return -1;
}
- if(muxer_init(de->de_mux, ss, lang_str_get(de->de_title, NULL))) {
+ if(muxer_init(muxer, ss, lang_str_get(de->de_title, NULL))) {
dvr_rec_fatal_error(de, "Unable to init file");
return -1;
}
if(cfg->dvr_tag_files && de->de_bcast) {
- if(muxer_write_meta(de->de_mux, de->de_bcast)) {
+ if(muxer_write_meta(muxer, de->de_bcast)) {
dvr_rec_fatal_error(de, "Unable to write meta data");
return -1;
}
@@ -438,7 +451,8 @@ dvr_thread(void *aux)
{
dvr_entry_t *de = aux;
dvr_config_t *cfg = de->de_config;
- streaming_queue_t *sq = &de->de_sq;
+ profile_chain_t *prch = de->de_chain;
+ streaming_queue_t *sq = &prch->prch_sq;
streaming_message_t *sm;
th_pkt_t *pkt;
int run = 1;
@@ -482,12 +496,12 @@ dvr_thread(void *aux)
break;
if(commercial != pkt->pkt_commercial)
- muxer_add_marker(de->de_mux);
+ muxer_add_marker(prch->prch_muxer);
commercial = pkt->pkt_commercial;
if(started) {
- muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data);
+ muxer_write_pkt(prch->prch_muxer, sm->sm_type, sm->sm_data);
sm->sm_data = NULL;
}
break;
@@ -495,14 +509,14 @@ dvr_thread(void *aux)
case SMT_MPEGTS:
if(started) {
dvr_rec_set_state(de, DVR_RS_RUNNING, 0);
- muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data);
+ muxer_write_pkt(prch->prch_muxer, sm->sm_type, sm->sm_data);
sm->sm_data = NULL;
}
break;
case SMT_START:
if(started &&
- muxer_reconfigure(de->de_mux, sm->sm_data) < 0) {
+ muxer_reconfigure(prch->prch_muxer, sm->sm_data) < 0) {
tvhlog(LOG_WARNING,
"dvr", "Unable to reconfigure \"%s\"",
de->de_filename ?: lang_str_get(de->de_title, NULL));
@@ -607,7 +621,7 @@ dvr_thread(void *aux)
}
pthread_mutex_unlock(&sq->sq_mutex);
- if(de->de_mux)
+ if(prch->prch_muxer)
dvr_thread_epilog(de);
return NULL;
@@ -671,9 +685,11 @@ dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc)
static void
dvr_thread_epilog(dvr_entry_t *de)
{
- muxer_close(de->de_mux);
- muxer_destroy(de->de_mux);
- de->de_mux = NULL;
+ profile_chain_t *prch = de->de_chain;
+
+ muxer_close(prch->prch_muxer);
+ muxer_destroy(prch->prch_muxer);
+ prch->prch_muxer = NULL;
dvr_config_t *cfg = de->de_config;
if(cfg && cfg->dvr_postproc && de->de_filename)
diff --git a/src/main.c b/src/main.c
index a83ad9dc..15ea889b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -69,6 +69,7 @@
#include "libav.h"
#include "plumbing/transcoding.h"
#endif
+#include "profile.h"
#ifdef PLATFORM_LINUX
#include
@@ -816,6 +817,8 @@ main(int argc, char **argv)
transcoding_init();
#endif
+ profile_init();
+
imagecache_init();
http_client_init(opt_user_agent);
@@ -939,6 +942,7 @@ main(int argc, char **argv)
tvhftrace("main", dvb_done);
tvhftrace("main", lang_str_done);
tvhftrace("main", esfilter_done);
+ tvhftrace("main", profile_done);
tvhftrace("main", intlconv_done);
tvhftrace("main", urlparse_done);
tvhftrace("main", idnode_done);
diff --git a/src/muxer.c b/src/muxer.c
index 033c4ac2..d673b611 100644
--- a/src/muxer.c
+++ b/src/muxer.c
@@ -159,6 +159,7 @@ muxer_container_type2txt(muxer_container_type_t mc)
}
+#if 0
/**
* Get a list of supported containers
*/
@@ -190,6 +191,7 @@ muxer_container_list(htsmsg_t *array)
return c;
}
+#endif
/**
@@ -237,25 +239,25 @@ muxer_container_mime2type(const char *str)
* Create a new muxer
*/
muxer_t*
-muxer_create(muxer_container_type_t mc, const muxer_config_t *m_cfg)
+muxer_create(const muxer_config_t *m_cfg)
{
muxer_t *m;
assert(m_cfg);
- m = pass_muxer_create(mc, m_cfg);
+ m = pass_muxer_create(m_cfg);
if(!m)
- m = tvh_muxer_create(mc, m_cfg);
+ m = tvh_muxer_create(m_cfg);
#if CONFIG_LIBAV
if(!m)
- m = lav_muxer_create(mc, m_cfg);
+ m = lav_muxer_create(m_cfg);
#endif
if(!m) {
tvhlog(LOG_ERR, "mux", "Can't find a muxer that supports '%s' container",
- muxer_container_type2txt(mc));
+ muxer_container_type2txt(m_cfg->m_type));
return NULL;
}
diff --git a/src/muxer.h b/src/muxer.h
index 2c6b9651..8b7a279e 100644
--- a/src/muxer.h
+++ b/src/muxer.h
@@ -44,6 +44,8 @@ typedef enum {
/* Muxer configuration used when creating a muxer. */
typedef struct muxer_config {
+ int m_type; /* MC_* */
+
int m_rewrite_pat;
int m_rewrite_pmt;
int m_cache;
@@ -83,7 +85,6 @@ typedef struct muxer {
int m_eos; // End of stream
int m_errors; // Number of errors
- muxer_container_type_t m_container; // The type of the container
muxer_config_t m_config; // general configuration
} muxer_t;
@@ -97,10 +98,10 @@ muxer_container_type_t muxer_container_mime2type (const char *str);
const char* muxer_container_suffix(muxer_container_type_t mc, int video);
-int muxer_container_list(htsmsg_t *array);
+//int muxer_container_list(htsmsg_t *array);
// Muxer factory
-muxer_t *muxer_create(muxer_container_type_t mc, const muxer_config_t *m_cfg);
+muxer_t *muxer_create(const muxer_config_t *m_cfg);
// Wrapper functions
int muxer_open_file (muxer_t *m, const char *filename);
diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c
index aeaf6f58..28b7a73e 100644
--- a/src/muxer/muxer_libav.c
+++ b/src/muxer/muxer_libav.c
@@ -75,7 +75,7 @@ lav_muxer_add_stream(lav_muxer_t *lm,
c = st->codec;
c->codec_id = streaming_component_type2codec_id(ssc->ssc_type);
- switch(lm->m_container) {
+ switch(lm->m_config.m_type) {
case MC_MATROSKA:
st->time_base.num = 1000000;
st->time_base.den = 1;
@@ -205,7 +205,7 @@ lav_muxer_mime(muxer_t* m, const struct streaming_start *ss)
if(ssc->ssc_disabled)
continue;
- if(!lav_muxer_support_stream(m->m_container, ssc->ssc_type))
+ if(!lav_muxer_support_stream(m->m_config.m_type, ssc->ssc_type))
continue;
has_video |= SCT_ISVIDEO(ssc->ssc_type);
@@ -213,9 +213,9 @@ lav_muxer_mime(muxer_t* m, const struct streaming_start *ss)
}
if(has_video)
- return muxer_container_type2mime(m->m_container, 1);
+ return muxer_container_type2mime(m->m_config.m_type, 1);
else if(has_audio)
- return muxer_container_type2mime(m->m_container, 0);
+ return muxer_container_type2mime(m->m_config.m_type, 0);
else
return muxer_container_type2mime(MC_UNKNOWN, 0);
}
@@ -241,7 +241,7 @@ lav_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name)
av_dict_set(&oc->metadata, "service_name", name, 0);
av_dict_set(&oc->metadata, "service_provider", app, 0);
- if(lm->m_container == MC_MPEGTS)
+ if(lm->m_config.m_type == MC_MPEGTS)
lm->lm_h264_filter = av_bitstream_filter_init("h264_mp4toannexb");
oc->max_delay = 0.7 * AV_TIME_BASE;
@@ -252,10 +252,10 @@ lav_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name)
if(ssc->ssc_disabled)
continue;
- if(!lav_muxer_support_stream(lm->m_container, ssc->ssc_type)) {
+ if(!lav_muxer_support_stream(lm->m_config.m_type, ssc->ssc_type)) {
tvhlog(LOG_WARNING, "libav", "%s is not supported in %s",
streaming_component_type2txt(ssc->ssc_type),
- muxer_container_type2txt(lm->m_container));
+ muxer_container_type2txt(lm->m_config.m_type));
continue;
}
@@ -272,7 +272,7 @@ lav_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name)
return -1;
} else if(avformat_write_header(lm->lm_oc, NULL) < 0) {
tvhlog(LOG_ERR, "libav", "Failed to write %s header",
- muxer_container_type2txt(lm->m_container));
+ muxer_container_type2txt(lm->m_config.m_type));
lm->m_errors++;
return -1;
}
@@ -454,7 +454,7 @@ lav_muxer_close(muxer_t *m)
if(lm->lm_init && av_write_trailer(lm->lm_oc) < 0) {
tvhlog(LOG_WARNING, "libav", "Failed to write %s trailer",
- muxer_container_type2txt(lm->m_container));
+ muxer_container_type2txt(lm->m_config.m_type));
lm->m_errors++;
ret = -1;
}
@@ -493,18 +493,18 @@ lav_muxer_destroy(muxer_t *m)
* Create a new libavformat based muxer
*/
muxer_t*
-lav_muxer_create(muxer_container_type_t mc, const muxer_config_t *m_cfg)
+lav_muxer_create(const muxer_config_t *m_cfg)
{
const char *mux_name;
lav_muxer_t *lm;
AVOutputFormat *fmt;
- switch(mc) {
+ switch(m_cfg->m_type) {
case MC_MPEGPS:
mux_name = "dvd";
break;
default:
- mux_name = muxer_container_type2txt(mc);
+ mux_name = muxer_container_type2txt(m_cfg->m_type);
break;
}
@@ -525,7 +525,6 @@ lav_muxer_create(muxer_container_type_t mc, const muxer_config_t *m_cfg)
lm->m_write_pkt = lav_muxer_write_pkt;
lm->m_close = lav_muxer_close;
lm->m_destroy = lav_muxer_destroy;
- lm->m_container = mc;
lm->lm_oc = avformat_alloc_context();
lm->lm_oc->oformat = fmt;
lm->lm_fd = -1;
diff --git a/src/muxer/muxer_libav.h b/src/muxer/muxer_libav.h
index e8e719bf..4c9ec609 100644
--- a/src/muxer/muxer_libav.h
+++ b/src/muxer/muxer_libav.h
@@ -21,7 +21,6 @@
#include "muxer.h"
-muxer_t* lav_muxer_create
- (muxer_container_type_t mc, const muxer_config_t* m_cfg);
+muxer_t* lav_muxer_create (const muxer_config_t* m_cfg);
#endif
diff --git a/src/muxer/muxer_pass.c b/src/muxer/muxer_pass.c
index 18b767a1..4985b3a1 100644
--- a/src/muxer/muxer_pass.c
+++ b/src/muxer/muxer_pass.c
@@ -562,11 +562,11 @@ pass_muxer_destroy(muxer_t *m)
* Create a new passthrough muxer
*/
muxer_t*
-pass_muxer_create(muxer_container_type_t mc, const muxer_config_t *m_cfg)
+pass_muxer_create(const muxer_config_t *m_cfg)
{
pass_muxer_t *pm;
- if(mc != MC_PASS && mc != MC_RAW)
+ if(m_cfg->m_type != MC_PASS && m_cfg->m_type != MC_RAW)
return NULL;
pm = calloc(1, sizeof(pass_muxer_t));
diff --git a/src/muxer/muxer_pass.h b/src/muxer/muxer_pass.h
index 637d312d..af52b801 100644
--- a/src/muxer/muxer_pass.h
+++ b/src/muxer/muxer_pass.h
@@ -21,7 +21,6 @@
#include "muxer.h"
-muxer_t* pass_muxer_create
- (muxer_container_type_t mc, const muxer_config_t* m_cfg);
+muxer_t* pass_muxer_create (const muxer_config_t* m_cfg);
#endif
diff --git a/src/muxer/muxer_tvh.c b/src/muxer/muxer_tvh.c
index f621f954..02e9edc2 100644
--- a/src/muxer/muxer_tvh.c
+++ b/src/muxer/muxer_tvh.c
@@ -56,9 +56,9 @@ tvh_muxer_mime(muxer_t* m, const struct streaming_start *ss)
}
if(has_video)
- return muxer_container_type2mime(m->m_container, 1);
+ return muxer_container_type2mime(m->m_config.m_type, 1);
else if(has_audio)
- return muxer_container_type2mime(m->m_container, 0);
+ return muxer_container_type2mime(m->m_config.m_type, 0);
else
return muxer_container_type2mime(MC_UNKNOWN, 0);
}
@@ -223,11 +223,11 @@ tvh_muxer_destroy(muxer_t *m)
* Create a new builtin muxer
*/
muxer_t*
-tvh_muxer_create(muxer_container_type_t mc, const muxer_config_t *m_cfg)
+tvh_muxer_create(const muxer_config_t *m_cfg)
{
tvh_muxer_t *tm;
- if(mc != MC_MATROSKA && mc != MC_WEBM)
+ if(m_cfg->m_type != MC_MATROSKA && m_cfg->m_type != MC_WEBM)
return NULL;
tm = calloc(1, sizeof(tvh_muxer_t));
@@ -241,8 +241,7 @@ tvh_muxer_create(muxer_container_type_t mc, const muxer_config_t *m_cfg)
tm->m_write_pkt = tvh_muxer_write_pkt;
tm->m_close = tvh_muxer_close;
tm->m_destroy = tvh_muxer_destroy;
- tm->m_container = mc;
- tm->tm_ref = mk_mux_create((muxer_t *)tm, mc == MC_WEBM);
+ tm->tm_ref = mk_mux_create((muxer_t *)tm, m_cfg->m_type == MC_WEBM);
return (muxer_t*)tm;
}
diff --git a/src/muxer/muxer_tvh.h b/src/muxer/muxer_tvh.h
index 488e443a..6c8b92fa 100644
--- a/src/muxer/muxer_tvh.h
+++ b/src/muxer/muxer_tvh.h
@@ -21,7 +21,6 @@
#include "muxer.h"
-muxer_t* tvh_muxer_create
- (muxer_container_type_t mc, const muxer_config_t* m_cfg);
+muxer_t* tvh_muxer_create (const muxer_config_t* m_cfg);
#endif
diff --git a/src/profile.c b/src/profile.c
new file mode 100644
index 00000000..bc2b4728
--- /dev/null
+++ b/src/profile.c
@@ -0,0 +1,541 @@
+/*
+ * tvheadend, Stream Profile
+ * Copyright (C) 2014 Jaroslav Kysela
+ *
+ * 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 .
+ */
+
+#include "tvheadend.h"
+#include "settings.h"
+#include "profile.h"
+#include "streaming.h"
+#include "plumbing/tsfix.h"
+#include "plumbing/globalheaders.h"
+#include "dvr/dvr.h"
+
+profile_builders_queue profile_builders;
+
+struct profile_entry_queue profiles;
+
+static profile_t *profile_default;
+
+static void profile_class_save ( idnode_t *in );
+
+/*
+ *
+ */
+
+void
+profile_register(const idclass_t *clazz, profile_builder_t builder)
+{
+ profile_build_t *pb = calloc(1, sizeof(*pb));
+ pb->clazz = clazz;
+ pb->build = builder;
+ LIST_INSERT_HEAD(&profile_builders, pb, link);
+}
+
+static profile_build_t *
+profile_class_find(const char *name)
+{
+ profile_build_t *pb;
+ LIST_FOREACH(pb, &profile_builders, link) {
+ if (strcmp(pb->clazz->ic_class, name) == 0)
+ return pb;
+ }
+ return NULL;
+}
+
+profile_t *
+profile_create
+ (const char *uuid, htsmsg_t *conf, int save)
+{
+ profile_t *pro = NULL;
+ profile_build_t *pb = NULL;
+ const char *s;
+
+ lock_assert(&global_lock);
+
+ if ((s = htsmsg_get_str(conf, "class")) != NULL)
+ pb = profile_class_find(s);
+ if (pb == NULL) {
+ tvherror("profile", "wrong class %s!", s);
+ abort();
+ }
+ pro = pb->build();
+ if (pro == NULL) {
+ tvherror("profile", "Profile class %s is not available!", s);
+ return NULL;
+ }
+ LIST_INIT(&pro->pro_dvr_configs);
+ if (idnode_insert(&pro->pro_id, uuid, pb->clazz, 0)) {
+ if (uuid)
+ tvherror("profile", "invalid uuid '%s'", uuid);
+ free(pro);
+ return NULL;
+ }
+ if (conf) {
+ int b;
+ idnode_load(&pro->pro_id, conf);
+ if (!htsmsg_get_bool(conf, "shield", &b))
+ pro->pro_shield = !!b;
+ }
+ TAILQ_INSERT_TAIL(&profiles, pro, pro_link);
+ if (save)
+ profile_class_save((idnode_t *)pro);
+ if (pro->pro_conf_changed)
+ pro->pro_conf_changed(pro);
+ return pro;
+}
+
+static void
+profile_delete(profile_t *pro, int delconf)
+{
+ pro->pro_enabled = 0;
+ if (pro->pro_conf_changed)
+ pro->pro_conf_changed(pro);
+ if (delconf)
+ hts_settings_remove("profile/%s", idnode_uuid_as_str(&pro->pro_id));
+ TAILQ_REMOVE(&profiles, pro, pro_link);
+ idnode_unlink(&pro->pro_id);
+ dvr_config_destroy_by_profile(pro, delconf);
+ if (pro->pro_free)
+ pro->pro_free(pro);
+ free(pro->pro_name);
+ free(pro->pro_comment);
+ free(pro);
+}
+
+static void
+profile_class_save ( idnode_t *in )
+{
+ profile_t *pro = (profile_t *)in;
+ htsmsg_t *c = htsmsg_create_map();
+ idnode_save(in, c);
+ if (pro->pro_shield)
+ htsmsg_add_bool(c, "shield", 1);
+ hts_settings_save(c, "profile/%s", idnode_uuid_as_str(in));
+ htsmsg_destroy(c);
+ if (pro->pro_conf_changed)
+ pro->pro_conf_changed(pro);
+}
+
+static const char *
+profile_class_get_title ( idnode_t *in )
+{
+ profile_t *pro = (profile_t *)in;
+ static char buf[32];
+ if (pro->pro_name && pro->pro_name[0])
+ return pro->pro_name;
+ snprintf(buf, sizeof(buf), "%s", in->in_class->ic_caption);
+ return buf;
+}
+
+static void
+profile_class_delete(idnode_t *self)
+{
+ profile_t *pro = (profile_t *)self;
+ if (pro->pro_shield)
+ return;
+ profile_delete(pro, 1);
+}
+
+static const void *
+profile_class_class_get(void *o)
+{
+ profile_t *pro = o;
+ static const char *ret;
+ ret = pro->pro_id.in_class->ic_class;
+ return &ret;
+}
+
+static int
+profile_class_class_set(void *o, const void *v)
+{
+ /* just ignore, create fcn does the right job */
+ return 0;
+}
+
+static const void *
+profile_class_default_get(void *o)
+{
+ static int res;
+ res = o == profile_default;
+ return &res;
+}
+
+static int
+profile_class_default_set(void *o, const void *v)
+{
+ profile_t *pro = o, *old;
+ if (*(int *)v && pro != profile_default) {
+ old = profile_default;
+ profile_default = pro;
+ if (old)
+ profile_class_save(&old->pro_id);
+ return 1;
+ }
+ return 0;
+}
+
+const idclass_t profile_class =
+{
+ .ic_class = "profile",
+ .ic_caption = "Stream Profile",
+ .ic_save = profile_class_save,
+ .ic_event = "profile",
+ .ic_get_title = profile_class_get_title,
+ .ic_delete = profile_class_delete,
+ .ic_properties = (const property_t[]){
+ {
+ .type = PT_STR,
+ .id = "class",
+ .name = "Class",
+ .opts = PO_RDONLY | PO_HIDDEN,
+ .get = profile_class_class_get,
+ .set = profile_class_class_set,
+ },
+ {
+ .type = PT_BOOL,
+ .id = "enabled",
+ .name = "Enabled",
+ .off = offsetof(profile_t, pro_enabled),
+ },
+ {
+ .type = PT_BOOL,
+ .id = "default",
+ .name = "Default",
+ .set = profile_class_default_set,
+ .get = profile_class_default_get,
+ },
+ {
+ .type = PT_STR,
+ .id = "name",
+ .name = "Profile Name",
+ .off = offsetof(profile_t, pro_name),
+ .notify = idnode_notify_title_changed,
+ },
+ {
+ .type = PT_STR,
+ .id = "comment",
+ .name = "Comment",
+ .off = offsetof(profile_t, pro_comment),
+ },
+ { }
+ }
+};
+
+/*
+ *
+ */
+const char *
+profile_get_name(profile_t *pro)
+{
+ if (pro->pro_name && *pro->pro_name) return pro->pro_name;
+ return "";
+}
+
+/*
+ *
+ */
+profile_t *
+profile_find_by_name(const char *name)
+{
+ profile_t *pro;
+
+ lock_assert(&global_lock);
+
+ if (!name)
+ return profile_default;
+
+ TAILQ_FOREACH(pro, &profiles, pro_link) {
+ if (!strcmp(pro->pro_name, name))
+ return pro;
+ }
+
+ return profile_default;
+}
+
+/*
+ *
+ */
+char *
+profile_validate_name(const char *name)
+{
+ profile_t *pro;
+
+ lock_assert(&global_lock);
+
+ TAILQ_FOREACH(pro, &profiles, pro_link) {
+ if (name && !strcmp(pro->pro_name, name))
+ return strdup(name);
+ }
+
+ if (profile_default)
+ return strdup(profile_default->pro_name);
+
+ return NULL;
+}
+
+/*
+ *
+ */
+htsmsg_t *
+profile_class_get_list(void *o)
+{
+ htsmsg_t *m = htsmsg_create_map();
+ htsmsg_add_str(m, "type", "api");
+ htsmsg_add_str(m, "uri", "profile/list");
+ htsmsg_add_str(m, "event", "profile");
+ return m;
+}
+
+/*
+ *
+ */
+int
+profile_chain_raw_open(profile_chain_t *prch, size_t qsize)
+{
+ muxer_config_t c;
+
+ memset(&c, 0, sizeof(c));
+ c.m_type = MC_RAW;
+ memset(prch, 0, sizeof(*prch));
+ prch->prch_flags = SUBSCRIPTION_RAW_MPEGTS;
+ streaming_queue_init(&prch->prch_sq, SMT_PACKET, qsize);
+ prch->prch_st = &prch->prch_sq.sq_st;
+ prch->prch_muxer = muxer_create(&c);
+ return 0;
+}
+
+/*
+ *
+ */
+void
+profile_chain_close(profile_chain_t *prch)
+{
+ if (prch->prch_tsfix)
+ tsfix_destroy(prch->prch_tsfix);
+ if (prch->prch_gh)
+ globalheaders_destroy(prch->prch_gh);
+ if (prch->prch_muxer)
+ muxer_destroy(prch->prch_muxer);
+ streaming_queue_deinit(&prch->prch_sq);
+}
+
+/*
+ * MPEG-TS passthrough muxer
+ */
+typedef struct profile_mpegts {
+ profile_t;
+ int pro_rewrite_pmt;
+ int pro_rewrite_pat;
+} profile_mpegts_t;
+
+const idclass_t profile_mpegts_pass_class =
+{
+ .ic_super = &profile_class,
+ .ic_class = "profile-mpegts",
+ .ic_caption = "MPEG-TS Pass-through",
+ .ic_properties = (const property_t[]){
+ {
+ .type = PT_BOOL,
+ .id = "rewrite_pmt",
+ .name = "Rewrite PMT",
+ .off = offsetof(profile_mpegts_t, pro_rewrite_pmt),
+ .def.i = 1,
+ },
+ {
+ .type = PT_BOOL,
+ .id = "rewrite_pat",
+ .name = "Rewrite PAT",
+ .off = offsetof(profile_mpegts_t, pro_rewrite_pat),
+ .def.i = 1,
+ },
+ { }
+ }
+};
+
+static int
+profile_mpegts_pass_open(profile_t *_pro, profile_chain_t *prch,
+ muxer_config_t *m_cfg, size_t qsize)
+{
+ profile_mpegts_t *pro = (profile_mpegts_t *)_pro;
+ muxer_config_t c;
+
+ if (m_cfg)
+ c = *m_cfg; /* do not alter the original parameter */
+ else
+ memset(&c, 0, sizeof(c));
+ if (c.m_type != MC_RAW)
+ c.m_type = MC_PASS;
+ c.m_rewrite_pat = pro->pro_rewrite_pat;
+ c.m_rewrite_pmt = pro->pro_rewrite_pmt;
+
+ memset(prch, 0, sizeof(*prch));
+ prch->prch_flags = SUBSCRIPTION_RAW_MPEGTS;
+ streaming_queue_init(&prch->prch_sq, SMT_PACKET, qsize);
+ prch->prch_muxer = muxer_create(&c);
+ prch->prch_st = &prch->prch_sq.sq_st;
+ return 0;
+}
+
+static muxer_container_type_t
+profile_mpegts_pass_get_mc(profile_t *_pro)
+{
+ return MC_PASS;
+}
+
+static profile_t *
+profile_mpegts_pass_builder(void)
+{
+ profile_mpegts_t *pro = calloc(1, sizeof(*pro));
+ pro->pro_open = profile_mpegts_pass_open;
+ pro->pro_get_mc = profile_mpegts_pass_get_mc;
+ return (profile_t *)pro;
+}
+
+/*
+ * Matroska muxer
+ */
+typedef struct profile_matroska {
+ profile_t;
+ int pro_webm;
+} profile_matroska_t;
+
+const idclass_t profile_matroska_class =
+{
+ .ic_super = &profile_class,
+ .ic_class = "profile-matroska",
+ .ic_caption = "Matroska (mkv)",
+ .ic_properties = (const property_t[]){
+ {
+ .type = PT_BOOL,
+ .id = "webm",
+ .name = "WEBM",
+ .off = offsetof(profile_matroska_t, pro_webm),
+ .def.i = 0,
+ },
+ { }
+ }
+};
+
+static int
+profile_matroska_open(profile_t *_pro, profile_chain_t *prch,
+ muxer_config_t *m_cfg, size_t qsize)
+{
+ profile_matroska_t *pro = (profile_matroska_t *)_pro;
+ muxer_config_t c;
+
+ if (m_cfg)
+ c = *m_cfg; /* do not alter the original parameter */
+ else
+ memset(&c, 0, sizeof(c));
+ if (c.m_type != MC_WEBM)
+ c.m_type = MC_MATROSKA;
+ if (pro->pro_webm)
+ c.m_type = MC_WEBM;
+
+ memset(prch, 0, sizeof(*prch));
+ streaming_queue_init(&prch->prch_sq, 0, qsize);
+ prch->prch_gh = globalheaders_create(&prch->prch_sq.sq_st);
+ prch->prch_tsfix = tsfix_create(prch->prch_gh);
+ prch->prch_muxer = muxer_create(&c);
+ prch->prch_st = prch->prch_tsfix;
+ return 0;
+}
+
+static muxer_container_type_t
+profile_matroska_get_mc(profile_t *_pro)
+{
+ profile_matroska_t *pro = (profile_matroska_t *)_pro;
+ if (pro->pro_webm)
+ return MC_WEBM;
+ return MC_MATROSKA;
+}
+
+static profile_t *
+profile_matroska_builder(void)
+{
+ profile_matroska_t *pro = calloc(1, sizeof(*pro));
+ pro->pro_open = profile_matroska_open;
+ pro->pro_get_mc = profile_matroska_get_mc;
+ return (profile_t *)pro;
+}
+
+/*
+ * Initialize
+ */
+void
+profile_init(void)
+{
+ htsmsg_t *c, *e;
+ htsmsg_field_t *f;
+
+ LIST_INIT(&profile_builders);
+ TAILQ_INIT(&profiles);
+
+ profile_register(&profile_mpegts_pass_class, profile_mpegts_pass_builder);
+ profile_register(&profile_matroska_class, profile_matroska_builder);
+
+ if ((c = hts_settings_load("profile")) != NULL) {
+ HTSMSG_FOREACH(f, c) {
+ if (!(e = htsmsg_field_get_map(f)))
+ continue;
+ (void)profile_create(f->hmf_name, e, 0);
+ }
+ htsmsg_destroy(c);
+ }
+
+ if (TAILQ_EMPTY(&profiles)) {
+ htsmsg_t *conf;
+
+ conf = htsmsg_create_map();
+ htsmsg_add_str (conf, "class", "profile-mpegts");
+ htsmsg_add_bool(conf, "enabled", 1);
+ htsmsg_add_bool(conf, "default", 1);
+ htsmsg_add_str (conf, "name", "pass");
+ htsmsg_add_str (conf, "comment", "MPEG-TS Pass-through");
+ htsmsg_add_bool(conf, "rewrite_pmt", 1);
+ htsmsg_add_bool(conf, "rewrite_pat", 1);
+ htsmsg_add_bool(conf, "shield", 1);
+ (void)profile_create(NULL, conf, 1);
+ htsmsg_destroy(conf);
+
+ conf = htsmsg_create_map();
+ htsmsg_add_str (conf, "class", "profile-matroska");
+ htsmsg_add_bool(conf, "enabled", 1);
+ htsmsg_add_str (conf, "name", "matroska");
+ htsmsg_add_str (conf, "comment", "Matroska");
+ htsmsg_add_bool(conf, "shield", 1);
+ (void)profile_create(NULL, conf, 1);
+ htsmsg_destroy(conf);
+ }
+}
+
+void
+profile_done(void)
+{
+ profile_t *pro;
+ profile_build_t *pb;
+
+ pthread_mutex_lock(&global_lock);
+ profile_default = NULL;
+ while ((pro = TAILQ_FIRST(&profiles)) != NULL)
+ profile_delete(pro, 0);
+ while ((pb = LIST_FIRST(&profile_builders)) != NULL) {
+ LIST_REMOVE(pb, link);
+ free(pb);
+ }
+ pthread_mutex_unlock(&global_lock);
+}
diff --git a/src/profile.h b/src/profile.h
new file mode 100644
index 00000000..6232e6bf
--- /dev/null
+++ b/src/profile.h
@@ -0,0 +1,102 @@
+/*
+ * tvheadend, Stream Profile
+ * Copyright (C) 2014 Jaroslav Kysela
+ *
+ * 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 .
+ */
+
+#ifndef __TVH_PROFILE_H__
+#define __TVH_PROFILE_H__
+
+#include "tvheadend.h"
+#include "idnode.h"
+#include "muxer.h"
+
+struct profile;
+struct muxer;
+struct streaming_target;
+
+extern const idclass_t profile_class;
+extern const idclass_t profile_mpegts_pass_class;
+extern const idclass_t profile_matroska_class;
+
+TAILQ_HEAD(profile_entry_queue, profile);
+
+extern struct profile_entry_queue profiles;
+
+typedef struct profile *(*profile_builder_t)(void);
+
+typedef struct profile_build {
+ LIST_ENTRY(profile_build) link;
+ const idclass_t *clazz;
+ profile_builder_t build;
+} profile_build_t;
+
+typedef LIST_HEAD(, profile_build) profile_builders_queue;
+
+extern profile_builders_queue profile_builders;
+
+typedef struct profile_chain {
+ int prch_flags;
+ struct streaming_queue prch_sq;
+ struct streaming_target *prch_st;
+ struct muxer *prch_muxer;
+ struct streaming_target *prch_gh;
+ struct streaming_target *prch_tsfix;
+} profile_chain_t;
+
+typedef struct profile {
+ idnode_t pro_id;
+ TAILQ_ENTRY(profile) pro_link;
+
+ LIST_HEAD(,dvr_config) pro_dvr_configs;
+
+ int pro_enabled;
+ int pro_shield;
+ char *pro_name;
+ char *pro_comment;
+
+ void (*pro_free)(struct profile *cac);
+ void (*pro_conf_changed)(struct profile *cac);
+ muxer_container_type_t (*pro_get_mc)(struct profile *cac);
+
+ int (*pro_open)(struct profile *cac, profile_chain_t *prch,
+ muxer_config_t *m_cfg, size_t qsize);
+} profile_t;
+
+void profile_register(const idclass_t *clazz, profile_builder_t builder);
+
+profile_t *profile_create
+ (const char *uuid, htsmsg_t *conf, int save);
+
+int profile_chain_raw_open(profile_chain_t *prch, size_t qsize);
+void profile_chain_close(profile_chain_t *prch);
+
+static inline profile_t *profile_find_by_uuid(const char *uuid)
+ { return (profile_t*)idnode_find(uuid, &profile_class, NULL); }
+profile_t *profile_find_by_name(const char *name);
+
+htsmsg_t * profile_class_get_list(void *o);
+
+char *profile_validate_name(const char *name);
+
+const char *profile_get_name(profile_t *pro);
+
+static inline muxer_container_type_t profile_get_mc(profile_t *pro)
+ { return pro->pro_get_mc(pro); }
+
+void profile_init(void);
+void profile_done(void);
+
+#endif /* __TVH_PROFILE_H__ */
diff --git a/src/service_mapper.c b/src/service_mapper.c
index d7fce5c0..30edd9e3 100644
--- a/src/service_mapper.c
+++ b/src/service_mapper.c
@@ -334,7 +334,7 @@ service_mapper_thread ( void *aux )
streaming_message_t *sm;
const char *err = NULL;
- streaming_queue_init(&sq, 0);
+ streaming_queue_init(&sq, 0, 0);
pthread_mutex_lock(&global_lock);
diff --git a/src/streaming.c b/src/streaming.c
index a09a484e..2da04ed7 100644
--- a/src/streaming.c
+++ b/src/streaming.c
@@ -75,7 +75,7 @@ streaming_queue_deliver(void *opauqe, streaming_message_t *sm)
*
*/
void
-streaming_queue_init2(streaming_queue_t *sq, int reject_filter, size_t maxsize)
+streaming_queue_init(streaming_queue_t *sq, int reject_filter, size_t maxsize)
{
streaming_target_init(&sq->sq_st, streaming_queue_deliver, sq, reject_filter);
@@ -86,16 +86,6 @@ streaming_queue_init2(streaming_queue_t *sq, int reject_filter, size_t maxsize)
sq->sq_maxsize = maxsize;
}
-/**
- *
- */
-void
-streaming_queue_init(streaming_queue_t *sq, int reject_filter)
-{
- streaming_queue_init2(sq, reject_filter, 0); // 0 = unlimited
-}
-
-
/**
*
*/
diff --git a/src/streaming.h b/src/streaming.h
index a5b5cdfa..8f1cde80 100644
--- a/src/streaming.h
+++ b/src/streaming.h
@@ -72,9 +72,7 @@ void streaming_target_init(streaming_target_t *st,
st_callback_t *cb, void *opaque,
int reject_filter);
-void streaming_queue_init(streaming_queue_t *sq, int reject_filter);
-
-void streaming_queue_init2
+void streaming_queue_init
(streaming_queue_t *sq, int reject_filter, size_t maxsize);
void streaming_queue_clear(struct streaming_message_queue *q);
diff --git a/src/timeshift.c b/src/timeshift.c
index c4bfa560..12e7601f 100644
--- a/src/timeshift.c
+++ b/src/timeshift.c
@@ -278,7 +278,7 @@ streaming_target_t *timeshift_create
tvh_pipe(O_NONBLOCK, &ts->rd_pipe);
/* Initialise input */
- streaming_queue_init(&ts->wr_queue, 0);
+ streaming_queue_init(&ts->wr_queue, 0, 0);
streaming_target_init(&ts->input, timeshift_input, ts, 0);
tvhthread_create(&ts->wr_thread, NULL, timeshift_writer, ts);
tvhthread_create(&ts->rd_thread, NULL, timeshift_reader, ts);
diff --git a/src/webui/static/app/esfilter.js b/src/webui/static/app/esfilter.js
index 84e97626..974bdcd3 100644
--- a/src/webui/static/app/esfilter.js
+++ b/src/webui/static/app/esfilter.js
@@ -1,14 +1,47 @@
/*
- * Elementary Stream Filters
+ * Stream Profiles, Elementary Stream Filters
*/
+tvheadend.caclient_builders = new Ext.data.JsonStore({
+ url: 'api/profile/builders',
+ root: 'entries',
+ fields: ['class', 'caption', 'props'],
+ id: 'class',
+ autoLoad: true
+});
+
tvheadend.esfilter_tab = function(panel)
{
+ tvheadend.idnode_form_grid(panel, {
+ url: 'api/profile',
+ clazz: 'profile',
+ comet: 'profile',
+ titleS: 'Stream Profile',
+ titleC: 'Stream Profile Name',
+ iconCls: 'stream_profile',
+ add: {
+ url: 'api/profile',
+ titleS: 'Stream Profile',
+ select: {
+ label: 'Type',
+ store: tvheadend.caclient_builders,
+ displayField: 'caption',
+ valueField: 'class',
+ propField: 'props'
+ },
+ create: { },
+ },
+ del: true,
+ help: function() {
+ new tvheadend.help('Stream Profile', 'config_profile.html');
+ }
+ });
+
tvheadend.idnode_grid(panel, {
url: 'api/esfilter/video',
titleS: 'Video Stream Filter',
titleP: 'Video Stream Filters',
- tabIndex: 0,
+ tabIndex: 1,
add: {
url: 'api/esfilter/video',
create: {}
@@ -24,7 +57,7 @@ tvheadend.esfilter_tab = function(panel)
url: 'api/esfilter/audio',
titleS: 'Audio Stream Filter',
titleP: 'Audio Stream Filters',
- tabIndex: 1,
+ tabIndex: 2,
add: {
url: 'api/esfilter/audio',
create: {}
@@ -40,7 +73,7 @@ tvheadend.esfilter_tab = function(panel)
url: 'api/esfilter/teletext',
titleS: 'Teletext Stream Filter',
titleP: 'Teletext Stream Filters',
- tabIndex: 2,
+ tabIndex: 3,
add: {
url: 'api/esfilter/teletext',
create: {}
@@ -56,7 +89,7 @@ tvheadend.esfilter_tab = function(panel)
url: 'api/esfilter/subtit',
titleS: 'Subtitle Stream Filter',
titleP: 'Subtitle Stream Filters',
- tabIndex: 3,
+ tabIndex: 4,
add: {
url: 'api/esfilter/subtit',
create: {}
@@ -72,7 +105,7 @@ tvheadend.esfilter_tab = function(panel)
url: 'api/esfilter/ca',
titleS: 'CA Stream Filter',
titleP: 'CA Stream Filters',
- tabIndex: 4,
+ tabIndex: 5,
add: {
url: 'api/esfilter/ca',
create: {}
@@ -88,7 +121,7 @@ tvheadend.esfilter_tab = function(panel)
url: 'api/esfilter/other',
titleS: 'Other Stream Filter',
titleP: 'Other Stream Filters',
- tabIndex: 5,
+ tabIndex: 6,
add: {
url: 'api/esfilter/other',
create: {}
diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js
index c96024b7..6b5008b2 100644
--- a/src/webui/static/app/tvheadend.js
+++ b/src/webui/static/app/tvheadend.js
@@ -378,6 +378,18 @@ function accessUpdate(o) {
cp.add(chepg);
+ /* Stream Config */
+ var stream = new Ext.TabPanel({
+ activeTab: 0,
+ autoScroll: true,
+ title: 'Stream',
+ iconCls: 'stream_config',
+ items: []
+ });
+ tvheadend.esfilter_tab(stream);
+
+ cp.add(stream);
+
/* DVR / Timeshift */
var tsdvr = new Ext.TabPanel({
activeTab: 0,
@@ -396,18 +408,6 @@ function accessUpdate(o) {
if (tvheadend.capabilities.indexOf('caclient') !== -1)
tvheadend.caclient(cp, null);
- /* Stream Config */
- var stream = new Ext.TabPanel({
- activeTab: 0,
- autoScroll: true,
- title: 'Stream',
- iconCls: 'stream_config',
- items: []
- });
- tvheadend.esfilter_tab(stream);
-
- cp.add(stream);
-
/* Debug */
tvheadend.tvhlog(cp);
diff --git a/src/webui/webui.c b/src/webui/webui.c
index 1c85657f..269eee55 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -37,15 +37,17 @@
#include "dvr/dvr.h"
#include "filebundle.h"
#include "streaming.h"
-#include "plumbing/tsfix.h"
-#include "plumbing/globalheaders.h"
#include "plumbing/transcoding.h"
+#include "profile.h"
#include "epg.h"
#include "muxer.h"
#include "imagecache.h"
#include "tcp.h"
#include "config.h"
#include "atomic.h"
+#if ENABLE_MPEGTS
+#include "input.h"
+#endif
#if defined(PLATFORM_LINUX)
#include
@@ -287,21 +289,20 @@ http_stream_postop ( void *tcp_id )
* HTTP stream loop
*/
static void
-http_stream_run(http_connection_t *hc, streaming_queue_t *sq,
- const char *name, muxer_container_type_t mc,
- th_subscription_t *s, muxer_config_t *mcfg)
+http_stream_run(http_connection_t *hc, profile_chain_t *prch,
+ const char *name, th_subscription_t *s)
{
streaming_message_t *sm;
int run = 1;
int started = 0;
- muxer_t *mux = NULL;
+ streaming_queue_t *sq = &prch->prch_sq;
+ muxer_t *mux = prch->prch_muxer;
int timeouts = 0, grace = 20;
struct timespec ts;
struct timeval tp;
int err = 0;
socklen_t errlen = sizeof(err);
- mux = muxer_create(mc, mcfg);
if(muxer_open_stream(mux, hc->hc_fd))
run = 0;
@@ -419,8 +420,6 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq,
if(started)
muxer_close(mux);
-
- muxer_destroy(mux);
}
@@ -433,14 +432,12 @@ http_channel_playlist(http_connection_t *hc, channel_t *channel)
htsbuf_queue_t *hq;
char buf[255];
const char *host;
- muxer_container_type_t mc;
+ char *profile;
if (http_access_verify_channel(hc, ACCESS_STREAMING, channel, 1))
return HTTP_STATUS_UNAUTHORIZED;
- mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux"));
- if(mc == MC_UNKNOWN)
- mc = dvr_config_find_by_name_default(NULL)->dvr_mc;
+ profile = profile_validate_name(http_arg_get(&hc->hc_req_args, "profile"));
hq = &hc->hc_reply;
host = http_arg_get(&hc->hc_args, "Host");
@@ -472,10 +469,11 @@ http_channel_playlist(http_connection_t *hc, channel_t *channel)
htsbuf_qprintf(hq, "&scodec=%s", streaming_component_type2txt(props.tp_scodec));
}
#endif
- htsbuf_qprintf(hq, "&mux=%s\n", muxer_container_type2txt(mc));
+ htsbuf_qprintf(hq, "&profile=%s\n", profile);
http_output_content(hc, "audio/x-mpegurl");
+ free(profile);
return 0;
}
@@ -490,14 +488,12 @@ http_tag_playlist(http_connection_t *hc, channel_tag_t *tag)
char buf[255];
channel_tag_mapping_t *ctm;
const char *host;
- muxer_container_type_t mc;
+ char *profile;
hq = &hc->hc_reply;
host = http_arg_get(&hc->hc_args, "Host");
- mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux"));
- if(mc == MC_UNKNOWN)
- mc = dvr_config_find_by_name_default(NULL)->dvr_mc;
+ profile = profile_validate_name(http_arg_get(&hc->hc_req_args, "profile"));
htsbuf_qprintf(hq, "#EXTM3U\n");
LIST_FOREACH(ctm, &tag->ct_ctms, ctm_tag_link) {
@@ -507,11 +503,12 @@ http_tag_playlist(http_connection_t *hc, channel_tag_t *tag)
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", channel_get_name(ctm->ctm_channel));
htsbuf_qprintf(hq, "http://%s%s?ticket=%s", host, buf,
access_ticket_create(buf, hc->hc_access));
- htsbuf_qprintf(hq, "&mux=%s\n", muxer_container_type2txt(mc));
+ htsbuf_qprintf(hq, "&profile=%s\n", profile);
}
http_output_content(hc, "audio/x-mpegurl");
+ free(profile);
return 0;
}
@@ -526,14 +523,12 @@ http_tag_list_playlist(http_connection_t *hc)
char buf[255];
channel_tag_t *ct;
const char *host;
- muxer_container_type_t mc;
+ char *profile;
hq = &hc->hc_reply;
host = http_arg_get(&hc->hc_args, "Host");
- mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux"));
- if(mc == MC_UNKNOWN)
- mc = dvr_config_find_by_name_default(NULL)->dvr_mc;
+ profile = profile_validate_name(http_arg_get(&hc->hc_req_args, "profile"));
htsbuf_qprintf(hq, "#EXTM3U\n");
TAILQ_FOREACH(ct, &channel_tags, ct_link) {
@@ -544,11 +539,12 @@ http_tag_list_playlist(http_connection_t *hc)
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ct->ct_name);
htsbuf_qprintf(hq, "http://%s%s?ticket=%s", host, buf,
access_ticket_create(buf, hc->hc_access));
- htsbuf_qprintf(hq, "&mux=%s\n", muxer_container_type2txt(mc));
+ htsbuf_qprintf(hq, "&profile=%s\n", profile);
}
http_output_content(hc, "audio/x-mpegurl");
+ free(profile);
return 0;
}
@@ -575,14 +571,12 @@ http_channel_list_playlist(http_connection_t *hc)
channel_t **chlist;
const char *host;
int idx = 0, count = 0;
- muxer_container_type_t mc;
+ char *profile;
hq = &hc->hc_reply;
host = http_arg_get(&hc->hc_args, "Host");
- mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux"));
- if(mc == MC_UNKNOWN)
- mc = dvr_config_find_by_name_default(NULL)->dvr_mc;
+ profile = profile_validate_name(http_arg_get(&hc->hc_req_args, "profile"));
CHANNEL_FOREACH(ch)
count++;
@@ -608,13 +602,14 @@ http_channel_list_playlist(http_connection_t *hc)
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", channel_get_name(ch));
htsbuf_qprintf(hq, "http://%s%s?ticket=%s", host, buf,
access_ticket_create(buf, hc->hc_access));
- htsbuf_qprintf(hq, "&mux=%s\n", muxer_container_type2txt(mc));
+ htsbuf_qprintf(hq, "&profile=%s\n", profile);
}
free(chlist);
http_output_content(hc, "audio/x-mpegurl");
+ free(profile);
return 0;
}
@@ -789,77 +784,51 @@ page_http_playlist(http_connection_t *hc, const char *remain, void *opaque)
static int
http_stream_service(http_connection_t *hc, service_t *service, int weight)
{
- streaming_queue_t sq;
th_subscription_t *s;
- streaming_target_t *gh;
- streaming_target_t *tsfix;
- streaming_target_t *st;
- dvr_config_t *cfg;
- muxer_container_type_t mc;
- int flags = SUBSCRIPTION_STREAMING;
+ profile_t *pro;
+ profile_chain_t prch;
const char *str;
size_t qsize;
const char *name;
char addrbuf[50];
void *tcp_id;
- int res = 0;
+ int res = HTTP_STATUS_BAD_REQUEST;
if(http_access_verify(hc, ACCESS_ADVANCED_STREAMING))
return HTTP_STATUS_UNAUTHORIZED;
- if((tcp_id = http_stream_preop(hc)) == NULL)
+ if(!(pro = profile_find_by_name(http_arg_get(&hc->hc_req_args, "profile"))))
return HTTP_STATUS_NOT_ALLOWED;
- cfg = dvr_config_find_by_name_default(NULL);
-
- /* Build muxer config - this takes the defaults from the default dvr config, which is a hack */
- mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux"));
- if(mc == MC_UNKNOWN) {
- mc = cfg->dvr_mc;
- }
+ if((tcp_id = http_stream_preop(hc)) == NULL)
+ return HTTP_STATUS_NOT_ALLOWED;
if ((str = http_arg_get(&hc->hc_req_args, "qsize")))
qsize = atoll(str);
else
qsize = 1500000;
- if(mc == MC_PASS || mc == MC_RAW) {
- streaming_queue_init2(&sq, SMT_PACKET, qsize);
- gh = NULL;
- tsfix = NULL;
- st = &sq.sq_st;
- flags |= SUBSCRIPTION_RAW_MPEGTS;
- } else {
- streaming_queue_init2(&sq, 0, qsize);
- gh = globalheaders_create(&sq.sq_st);
- tsfix = tsfix_create(gh);
- st = tsfix;
+ if (!pro->pro_open(pro, &prch, NULL, qsize)) {
+
+ tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50);
+
+ s = subscription_create_from_service(service, weight ?: 100, "HTTP",
+ prch.prch_st,
+ prch.prch_flags | SUBSCRIPTION_STREAMING,
+ addrbuf,
+ hc->hc_username,
+ http_arg_get(&hc->hc_args, "User-Agent"));
+ if(s) {
+ name = tvh_strdupa(service->s_nicename);
+ pthread_mutex_unlock(&global_lock);
+ http_stream_run(hc, &prch, name, s);
+ pthread_mutex_lock(&global_lock);
+ subscription_unsubscribe(s);
+ res = 0;
+ }
}
- tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50);
-
- s = subscription_create_from_service(service, weight ?: 100, "HTTP", st, flags,
- addrbuf,
- hc->hc_username,
- http_arg_get(&hc->hc_args, "User-Agent"));
- if(s) {
- name = tvh_strdupa(service->s_nicename);
- pthread_mutex_unlock(&global_lock);
- http_stream_run(hc, &sq, name, mc, s, &cfg->dvr_muxcnf);
- pthread_mutex_lock(&global_lock);
- subscription_unsubscribe(s);
- } else {
- res = HTTP_STATUS_BAD_REQUEST;
- }
-
- if(gh)
- globalheaders_destroy(gh);
-
- if(tsfix)
- tsfix_destroy(tsfix);
-
- streaming_queue_deinit(&sq);
-
+ profile_chain_close(&prch);
http_stream_postop(tcp_id);
return res;
}
@@ -870,17 +839,17 @@ http_stream_service(http_connection_t *hc, service_t *service, int weight)
* TODO: can't currently force this to be on a particular input
*/
#if ENABLE_MPEGTS
-#include "input.h"
static int
http_stream_mux(http_connection_t *hc, mpegts_mux_t *mm, int weight)
{
th_subscription_t *s;
- streaming_queue_t sq;
+ profile_chain_t prch;
+ size_t qsize;
const char *name;
char addrbuf[50];
- muxer_config_t muxcfg = { 0 };
void *tcp_id;
- int res = 0;
+ const char *str;
+ int res = HTTP_STATUS_BAD_REQUEST;
if(http_access_verify(hc, ACCESS_ADVANCED_STREAMING))
return HTTP_STATUS_UNAUTHORIZED;
@@ -888,27 +857,33 @@ http_stream_mux(http_connection_t *hc, mpegts_mux_t *mm, int weight)
if((tcp_id = http_stream_preop(hc)) == NULL)
return HTTP_STATUS_NOT_ALLOWED;
- streaming_queue_init(&sq, SMT_PACKET);
+ if ((str = http_arg_get(&hc->hc_req_args, "qsize")))
+ qsize = atoll(str);
+ else
+ qsize = 10000000;
- tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50);
- s = subscription_create_from_mux(mm, weight ?: 10, "HTTP", &sq.sq_st,
- SUBSCRIPTION_RAW_MPEGTS |
- SUBSCRIPTION_FULLMUX |
- SUBSCRIPTION_STREAMING,
- addrbuf, hc->hc_username,
- http_arg_get(&hc->hc_args, "User-Agent"), NULL);
- if (s) {
- name = tvh_strdupa(s->ths_title);
- pthread_mutex_unlock(&global_lock);
- http_stream_run(hc, &sq, name, MC_RAW, s, &muxcfg);
- pthread_mutex_lock(&global_lock);
- subscription_unsubscribe(s);
- } else {
- res = HTTP_STATUS_BAD_REQUEST;
+ if (!profile_chain_raw_open(&prch, qsize)) {
+
+ tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50);
+
+ s = subscription_create_from_mux(mm, weight ?: 10, "HTTP",
+ prch.prch_st,
+ prch.prch_flags |
+ SUBSCRIPTION_FULLMUX |
+ SUBSCRIPTION_STREAMING,
+ addrbuf, hc->hc_username,
+ http_arg_get(&hc->hc_args, "User-Agent"), NULL);
+ if (s) {
+ name = tvh_strdupa(s->ths_title);
+ pthread_mutex_unlock(&global_lock);
+ http_stream_run(hc, &prch, name, s);
+ pthread_mutex_lock(&global_lock);
+ subscription_unsubscribe(s);
+ res = 0;
+ }
}
- streaming_queue_deinit(&sq);
-
+ profile_chain_close(&prch);
http_stream_postop(tcp_id);
return res;
@@ -921,93 +896,50 @@ http_stream_mux(http_connection_t *hc, mpegts_mux_t *mm, int weight)
static int
http_stream_channel(http_connection_t *hc, channel_t *ch, int weight)
{
- streaming_queue_t sq;
th_subscription_t *s;
- streaming_target_t *gh;
- streaming_target_t *tsfix;
- streaming_target_t *st;
-#if ENABLE_LIBAV
- streaming_target_t *tr = NULL;
-#endif
- dvr_config_t *cfg;
- int flags = SUBSCRIPTION_STREAMING;
- muxer_container_type_t mc;
+ profile_t *pro;
+ profile_chain_t prch;
char *str;
size_t qsize;
const char *name;
char addrbuf[50];
void *tcp_id;
- int res = 0;
+ int res = HTTP_STATUS_BAD_REQUEST;
if (http_access_verify_channel(hc, ACCESS_STREAMING, ch, 1))
return HTTP_STATUS_UNAUTHORIZED;
- if((tcp_id = http_stream_preop(hc)) == NULL)
+ if(!(pro = profile_find_by_name(http_arg_get(&hc->hc_req_args, "profile"))))
return HTTP_STATUS_NOT_ALLOWED;
- cfg = dvr_config_find_by_name_default(NULL);
-
- /* Build muxer config - this takes the defaults from the default dvr config, which is a hack */
- mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux"));
- if(mc == MC_UNKNOWN) {
- mc = cfg->dvr_mc;
- }
+ if((tcp_id = http_stream_preop(hc)) == NULL)
+ return HTTP_STATUS_NOT_ALLOWED;
if ((str = http_arg_get(&hc->hc_req_args, "qsize")))
qsize = atoll(str);
else
qsize = 1500000;
- if(mc == MC_PASS || mc == MC_RAW) {
- streaming_queue_init2(&sq, SMT_PACKET, qsize);
- gh = NULL;
- tsfix = NULL;
- st = &sq.sq_st;
- flags |= SUBSCRIPTION_RAW_MPEGTS;
- } else {
- streaming_queue_init2(&sq, 0, qsize);
- gh = globalheaders_create(&sq.sq_st);
-#if ENABLE_LIBAV
- transcoder_props_t props;
- if(http_get_transcoder_properties(&hc->hc_req_args, &props)) {
- tr = transcoder_create(gh);
- transcoder_set_properties(tr, &props);
- tsfix = tsfix_create(tr);
- } else
-#endif
- tsfix = tsfix_create(gh);
- st = tsfix;
+ if (!pro->pro_open(pro, &prch, NULL, qsize)) {
+
+ tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50);
+
+ s = subscription_create_from_channel(ch, weight ?: 100, "HTTP",
+ prch.prch_st, prch.prch_flags | SUBSCRIPTION_STREAMING,
+ addrbuf, hc->hc_username,
+ http_arg_get(&hc->hc_args, "User-Agent"));
+
+ if(s) {
+ name = tvh_strdupa(channel_get_name(ch));
+ pthread_mutex_unlock(&global_lock);
+ http_stream_run(hc, &prch, name, s);
+ pthread_mutex_lock(&global_lock);
+ subscription_unsubscribe(s);
+ res = 0;
+ }
}
- tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50);
- s = subscription_create_from_channel(ch, weight ?: 100, "HTTP", st, flags,
- addrbuf,
- hc->hc_username,
- http_arg_get(&hc->hc_args, "User-Agent"));
-
- if(s) {
- name = tvh_strdupa(channel_get_name(ch));
- pthread_mutex_unlock(&global_lock);
- http_stream_run(hc, &sq, name, mc, s, &cfg->dvr_muxcnf);
- pthread_mutex_lock(&global_lock);
- subscription_unsubscribe(s);
- } else {
- res = HTTP_STATUS_BAD_REQUEST;
- }
-
- if(gh)
- globalheaders_destroy(gh);
-
-#if ENABLE_LIBAV
- if(tr)
- transcoder_destroy(tr);
-#endif
-
- if(tsfix)
- tsfix_destroy(tsfix);
-
- streaming_queue_deinit(&sq);
-
+ profile_chain_close(&prch);
http_stream_postop(tcp_id);
return res;