profile, transcode: move transcode config to profiles, add new profile type

This commit is contained in:
Jaroslav Kysela 2014-10-09 20:23:41 +02:00
parent 24818812ff
commit 811191c862
9 changed files with 381 additions and 153 deletions

View file

@ -34,9 +34,6 @@
#if ENABLE_TIMESHIFT
#include "timeshift.h"
#endif
#if ENABLE_LIBAV
#include "plumbing/transcoding.h"
#endif
#include <pthread.h>
#include <assert.h>
@ -193,9 +190,8 @@ typedef struct htsp_subscription {
streaming_target_t *hs_tshift;
#endif
#if ENABLE_LIBAV
streaming_target_t *hs_transcoder;
#endif
streaming_target_t *hs_work;
void (*hs_work_destroy)(streaming_target_t *);
htsp_msg_q_t hs_q;
@ -340,10 +336,8 @@ htsp_subscription_destroy(htsp_connection_t *htsp, htsp_subscription_t *hs)
if(hs->hs_tsfix != NULL)
tsfix_destroy(hs->hs_tsfix);
#if ENABLE_LIBAV
if(hs->hs_transcoder != NULL)
transcoder_destroy(hs->hs_transcoder);
#endif
if(hs->hs_work != NULL)
hs->hs_work_destroy(hs->hs_work);
#if ENABLE_TIMESHIFT
if(hs->hs_tshift)
@ -1763,27 +1757,13 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in)
#endif
#if ENABLE_LIBAV
if (transcoding_enabled) {
transcoder_props_t props;
const char *profile_id = htsmsg_get_str(in, "profile");
profile_t *pro = profile_find_by_uuid(profile_id) ?: profile_find_by_name(profile_id);
props.tp_vcodec = streaming_component_txt2type(htsmsg_get_str(in, "videoCodec"));
props.tp_acodec = streaming_component_txt2type(htsmsg_get_str(in, "audioCodec"));
props.tp_scodec = streaming_component_txt2type(htsmsg_get_str(in, "subtitleCodec"));
props.tp_resolution = htsmsg_get_u32_or_default(in, "maxResolution", 0);
props.tp_channels = htsmsg_get_u32_or_default(in, "channels", 0);
props.tp_bandwidth = htsmsg_get_u32_or_default(in, "bandwidth", 0);
if ((str = htsmsg_get_str(in, "language")))
strncpy(props.tp_language, str, 3);
if(props.tp_vcodec != SCT_UNKNOWN ||
props.tp_acodec != SCT_UNKNOWN ||
props.tp_scodec != SCT_UNKNOWN) {
st = hs->hs_transcoder = transcoder_create(st);
transcoder_set_properties(st, &props);
normts = 1;
}
hs->hs_work = profile_work(pro, st, &hs->hs_work_destroy);
if (hs->hs_work) {
st = hs->hs_work;
normts = 1;
}
#endif
@ -2142,27 +2122,23 @@ htsp_method_file_seek(htsp_connection_t *htsp, htsmsg_t *in)
return rep;
}
#if ENABLE_LIBAV
/**
*
*/
static htsmsg_t *
htsp_method_getCodecs(htsp_connection_t *htsp, htsmsg_t *in)
htsp_method_getProfiles(htsp_connection_t *htsp, htsmsg_t *in)
{
htsmsg_t *out, *l;
l = htsmsg_create_list();
transcoder_get_capabilities(l);
profile_get_htsp_list(l);
out = htsmsg_create_map();
htsmsg_add_msg(out, "encoders", l);
htsmsg_add_msg(out, "profiles", l);
return out;
}
#endif
/**
* HTSP methods
@ -2198,9 +2174,7 @@ struct {
{ "subscriptionSpeed", htsp_method_speed, ACCESS_STREAMING},
{ "subscriptionLive", htsp_method_live, ACCESS_STREAMING},
{ "subscriptionFilterStream", htsp_method_filter_stream, ACCESS_STREAMING},
#if ENABLE_LIBAV
{ "getCodecs", htsp_method_getCodecs, ACCESS_STREAMING},
#endif
{ "getProfiles", htsp_method_getProfiles, ACCESS_STREAMING},
{ "fileOpen", htsp_method_file_open, ACCESS_RECORDER},
{ "fileRead", htsp_method_file_read, ACCESS_RECORDER},
{ "fileClose", htsp_method_file_close, ACCESS_RECORDER},

View file

@ -147,9 +147,6 @@ const tvh_caps_t tvheadend_capabilities[] = {
#if ENABLE_SATIP_CLIENT
{ "satip_client", NULL },
#endif
#if ENABLE_LIBAV
{ "transcoding", &transcoding_enabled },
#endif
#if ENABLE_IMAGECACHE
{ "imagecache", (uint32_t*)&imagecache_conf.enabled },
#endif

View file

@ -1321,12 +1321,12 @@ transcoder_destroy(streaming_target_t *st)
/**
*
*/
void
transcoder_get_capabilities(htsmsg_t *array)
htsmsg_t *
transcoder_get_capabilities(void)
{
AVCodec *p = NULL;
const char *name;
streaming_component_type_t sct;
htsmsg_t *array = htsmsg_create_list();
while ((p = av_codec_next(p))) {
@ -1340,9 +1340,9 @@ transcoder_get_capabilities(htsmsg_t *array)
if (sct == SCT_NONE)
continue;
name = streaming_component_type2txt(sct);
htsmsg_add_str(array, NULL, name);
htsmsg_add_s32(array, NULL, sct);
}
return array;
}

View file

@ -36,7 +36,7 @@ extern uint32_t transcoding_enabled;
streaming_target_t *transcoder_create (streaming_target_t *output);
void transcoder_destroy(streaming_target_t *tr);
void transcoder_get_capabilities(htsmsg_t *array);
htsmsg_t *transcoder_get_capabilities(void);
void transcoder_set_properties (streaming_target_t *tr,
transcoder_props_t *prop);

View file

@ -22,6 +22,10 @@
#include "streaming.h"
#include "plumbing/tsfix.h"
#include "plumbing/globalheaders.h"
#if ENABLE_LIBAV
#include "lang_codes.h"
#include "plumbing/transcoding.h"
#endif
#include "dvr/dvr.h"
profile_builders_queue profile_builders;
@ -300,6 +304,26 @@ profile_class_get_list(void *o)
return m;
}
/*
*
*/
void
profile_get_htsp_list(htsmsg_t *array)
{
profile_t *pro;
htsmsg_t *m;
TAILQ_FOREACH(pro, &profiles, pro_link) {
if (pro->pro_work) {
m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&pro->pro_id));
htsmsg_add_str(m, "name", pro->pro_name ?: "");
htsmsg_add_str(m, "comment", pro->pro_comment ?: "");
htsmsg_add_msg(array, NULL, m);
}
}
}
/*
*
*/
@ -328,6 +352,10 @@ profile_chain_close(profile_chain_t *prch)
tsfix_destroy(prch->prch_tsfix);
if (prch->prch_gh)
globalheaders_destroy(prch->prch_gh);
#if ENABLE_LIBAV
if (prch->prch_transcoder != NULL)
transcoder_destroy(prch->prch_transcoder);
#endif
if (prch->prch_muxer)
muxer_destroy(prch->prch_muxer);
streaming_queue_deinit(&prch->prch_sq);
@ -473,6 +501,319 @@ profile_matroska_builder(void)
return (profile_t *)pro;
}
#if ENABLE_LIBAV
/*
* Transcoding + packet-like muxers
*/
typedef struct profile_transcode {
profile_t;
int pro_mc;
uint32_t pro_resolution;
uint32_t pro_channels;
uint32_t pro_bandwidth;
char *pro_language;
int pro_vcodec;
int pro_acodec;
int pro_scodec;
} profile_transcode_t;
static htsmsg_t *
profile_class_mc_list ( void *o )
{
static const struct strtab tab[] = {
{ "Not set", MC_UNKNOWN },
{ "Matroska (mkv)", MC_MATROSKA, },
{ "WEBM", MC_WEBM, },
{ "MPEG-TS", MC_MPEGTS },
{ "MPEG-PS (DVD)", MC_MPEGPS },
};
return strtab2htsmsg(tab);
}
static htsmsg_t *
profile_class_channels_list ( void *o )
{
static const struct strtab tab[] = {
{ "Copy layout", 0 },
{ "Mono", 1 },
{ "Stereo", 2 },
{ "Surround (2 Front, Rear Mono)", 3 },
{ "Quad (4.0)", 4 },
{ "5.0", 5 },
{ "5.1", 6 },
{ "6.1", 7 },
{ "7.1", 8 }
};
return strtab2htsmsg(tab);
}
static htsmsg_t *
profile_class_language_list(void *o)
{
htsmsg_t *l = htsmsg_create_list();
const lang_code_t *lc = lang_codes;
char buf[128];
while (lc->code2b) {
htsmsg_t *e = htsmsg_create_map();
if (!strcmp(lc->code2b, "und")) {
htsmsg_add_str(e, "key", "");
htsmsg_add_str(e, "val", "Use original");
} else {
htsmsg_add_str(e, "key", lc->code2b);
snprintf(buf, sizeof(buf), "%s (%s)", lc->desc, lc->code2b);
buf[sizeof(buf)-1] = '\0';
htsmsg_add_str(e, "val", buf);
}
htsmsg_add_msg(l, NULL, e);
lc++;
}
return l;
}
static inline int
profile_class_check_sct(htsmsg_t *c, int sct)
{
htsmsg_field_t *f;
int64_t x;
HTSMSG_FOREACH(f, c)
if (!htsmsg_field_get_s64(f, &x))
if (x == sct)
return 1;
return 0;
}
static htsmsg_t *
profile_class_vcodec_list(void *o)
{
htsmsg_t *l = htsmsg_create_list(), *e, *c;
int i;
e = htsmsg_create_map();
htsmsg_add_s32(e, "key", -1);
htsmsg_add_str(e, "val", "Do not use");
htsmsg_add_msg(l, NULL, e);
e = htsmsg_create_map();
htsmsg_add_s32(e, "key", 0);
htsmsg_add_str(e, "val", "Copy codec type");
htsmsg_add_msg(l, NULL, e);
c = transcoder_get_capabilities();
for (i = 0; i <= SCT_LAST; i++) {
if (!SCT_ISVIDEO(i))
continue;
if (!profile_class_check_sct(c, i))
continue;
e = htsmsg_create_map();
htsmsg_add_u32(e, "key", i);
htsmsg_add_str(e, "val", streaming_component_type2txt(i));
htsmsg_add_msg(l, NULL, e);
}
htsmsg_destroy(c);
return l;
}
static htsmsg_t *
profile_class_acodec_list(void *o)
{
htsmsg_t *l = htsmsg_create_list(), *e, *c;
int i;
e = htsmsg_create_map();
htsmsg_add_s32(e, "key", -1);
htsmsg_add_str(e, "val", "Do not use");
htsmsg_add_msg(l, NULL, e);
e = htsmsg_create_map();
htsmsg_add_s32(e, "key", 0);
htsmsg_add_str(e, "val", "Copy codec type");
htsmsg_add_msg(l, NULL, e);
c = transcoder_get_capabilities();
for (i = 0; i <= SCT_LAST; i++) {
if (!SCT_ISAUDIO(i))
continue;
if (!profile_class_check_sct(c, i))
continue;
e = htsmsg_create_map();
htsmsg_add_s32(e, "key", i);
htsmsg_add_str(e, "val", streaming_component_type2txt(i));
htsmsg_add_msg(l, NULL, e);
}
htsmsg_destroy(c);
return l;
}
static htsmsg_t *
profile_class_scodec_list(void *o)
{
htsmsg_t *l = htsmsg_create_list(), *e, *c;
int i;
e = htsmsg_create_map();
htsmsg_add_s32(e, "key", -1);
htsmsg_add_str(e, "val", "Do not use");
htsmsg_add_msg(l, NULL, e);
e = htsmsg_create_map();
htsmsg_add_s32(e, "key", 0);
htsmsg_add_str(e, "val", "Copy codec type");
htsmsg_add_msg(l, NULL, e);
c = transcoder_get_capabilities();
for (i = 0; i <= SCT_LAST; i++) {
if (!SCT_ISSUBTITLE(i))
continue;
if (!profile_class_check_sct(c, i))
continue;
e = htsmsg_create_map();
htsmsg_add_s32(e, "key", i);
htsmsg_add_str(e, "val", streaming_component_type2txt(i));
htsmsg_add_msg(l, NULL, e);
}
htsmsg_destroy(c);
return l;
}
const idclass_t profile_transcode_class =
{
.ic_super = &profile_class,
.ic_class = "profile-transcode",
.ic_caption = "Transcode",
.ic_properties = (const property_t[]){
{
.type = PT_INT,
.id = "container",
.name = "Container",
.off = offsetof(profile_transcode_t, pro_mc),
.def.i = MC_MATROSKA,
.list = profile_class_mc_list,
},
{
.type = PT_U32,
.id = "resolution",
.name = "Resolution",
.off = offsetof(profile_transcode_t, pro_resolution),
.def.u32 = 384,
},
{
.type = PT_U32,
.id = "channels",
.name = "Channels",
.off = offsetof(profile_transcode_t, pro_channels),
.def.u32 = 2,
.list = profile_class_channels_list,
},
{
.type = PT_STR,
.id = "language",
.name = "Language",
.off = offsetof(profile_transcode_t, pro_language),
.list = profile_class_language_list,
},
{
.type = PT_INT,
.id = "vcodec",
.name = "Video Codec",
.off = offsetof(profile_transcode_t, pro_vcodec),
.def.i = SCT_H264,
.list = profile_class_vcodec_list,
},
{
.type = PT_INT,
.id = "acodec",
.name = "Audio Codec",
.off = offsetof(profile_transcode_t, pro_acodec),
.def.i = SCT_VORBIS,
.list = profile_class_acodec_list,
},
{
.type = PT_INT,
.id = "scodec",
.name = "Subtitles Codec",
.off = offsetof(profile_transcode_t, pro_scodec),
.def.i = SCT_NONE,
.list = profile_class_scodec_list,
},
{ }
}
};
static void
profile_transcode_work_destroy(streaming_target_t *st)
{
if (st)
transcoder_destroy(st);
}
static streaming_target_t *
profile_transcode_work(profile_t *_pro, streaming_target_t *src,
void (**destroy)(streaming_target_t *st))
{
profile_transcode_t *pro = (profile_transcode_t *)_pro;
transcoder_props_t props;
memset(&props, 0, sizeof(props));
props.tp_vcodec = pro->pro_vcodec;
props.tp_acodec = pro->pro_acodec;
props.tp_scodec = pro->pro_scodec;
props.tp_resolution = pro->pro_resolution >= 240 ? pro->pro_resolution : 240;
props.tp_channels = pro->pro_channels;
props.tp_bandwidth = pro->pro_bandwidth >= 64 ? pro->pro_bandwidth : 64;
strncpy(props.tp_language, pro->pro_language ?: "", 3);
if (destroy)
*destroy = profile_transcode_work_destroy;
return transcoder_create(src);
}
static int
profile_transcode_open(profile_t *_pro, profile_chain_t *prch,
muxer_config_t *m_cfg, int flags, size_t qsize)
{
muxer_config_t c;
if (m_cfg)
c = *m_cfg; /* do not alter the original parameter */
else
memset(&c, 0, sizeof(c));
switch (c.m_type) {
case MC_MATROSKA:
case MC_WEBM:
case MC_MPEGTS:
case MC_MPEGPS:
break;
default:
c.m_type = MC_MATROSKA;
break;
}
memset(prch, 0, sizeof(*prch));
streaming_queue_init(&prch->prch_sq, 0, qsize);
prch->prch_transcoder = profile_transcode_work(_pro, &prch->prch_sq.sq_st, NULL);
prch->prch_gh = globalheaders_create(prch->prch_transcoder);
prch->prch_tsfix = tsfix_create(prch->prch_gh);
prch->prch_muxer = muxer_create(&c);
prch->prch_st = prch->prch_transcoder;
return 0;
}
static muxer_container_type_t
profile_transcode_get_mc(profile_t *_pro)
{
profile_transcode_t *pro = (profile_transcode_t *)_pro;
return pro->pro_mc;
}
static profile_t *
profile_transcode_builder(void)
{
profile_transcode_t *pro = calloc(1, sizeof(*pro));
pro->pro_work = profile_transcode_work;
pro->pro_open = profile_transcode_open;
pro->pro_get_mc = profile_transcode_get_mc;
return (profile_t *)pro;
}
#endif /* ENABLE_TRANSCODE */
/*
* Initialize
*/
@ -487,6 +828,9 @@ profile_init(void)
profile_register(&profile_mpegts_pass_class, profile_mpegts_pass_builder);
profile_register(&profile_matroska_class, profile_matroska_builder);
#if ENABLE_LIBAV
profile_register(&profile_transcode_class, profile_transcode_builder);
#endif
if ((c = hts_settings_load("profile")) != NULL) {
HTSMSG_FOREACH(f, c) {

View file

@ -31,8 +31,6 @@ extern const idclass_t profile_class;
extern const idclass_t profile_mpegts_pass_class;
extern const idclass_t profile_matroska_class;
#define PROFILE_TIMESHIFT (1<<0)
TAILQ_HEAD(profile_entry_queue, profile);
extern struct profile_entry_queue profiles;
@ -56,6 +54,9 @@ typedef struct profile_chain {
struct muxer *prch_muxer;
struct streaming_target *prch_gh;
struct streaming_target *prch_tsfix;
#if ENABLE_LIBAV
struct streaming_target *prch_transcoder;
#endif
} profile_chain_t;
typedef struct profile {
@ -73,6 +74,9 @@ typedef struct profile {
void (*pro_conf_changed)(struct profile *pro);
muxer_container_type_t (*pro_get_mc)(struct profile *pro);
struct streaming_target *(*pro_work)(struct profile *pro,
struct streaming_target *src,
void (**destroy)(struct streaming_target *));
int (*pro_open)(struct profile *pro, profile_chain_t *prch,
muxer_config_t *m_cfg, int flags, size_t qsize);
} profile_t;
@ -82,6 +86,12 @@ void profile_register(const idclass_t *clazz, profile_builder_t builder);
profile_t *profile_create
(const char *uuid, htsmsg_t *conf, int save);
static inline struct streaming_target *
profile_work(profile_t *pro, struct streaming_target *src,
void (**destroy)(struct streaming_target *st))
{ return pro && pro->pro_work ? pro->pro_work(pro, src, destroy) : NULL; }
static inline int
profile_chain_open(profile_t *pro, profile_chain_t *prch,
muxer_config_t *m_cfg, int flags, size_t qsize)
@ -102,6 +112,8 @@ 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_get_htsp_list(htsmsg_t *array);
void profile_init(void);
void profile_done(void);

View file

@ -43,7 +43,7 @@ tvheadend.miscconf = function(panel, index) {
[
'muxconfpath', 'language',
'tvhtime_update_enabled', 'tvhtime_ntp_enabled',
'tvhtime_tolerance', 'transcoding_enabled',
'tvhtime_tolerance',
'piconpath'
]);
@ -203,28 +203,6 @@ tvheadend.miscconf = function(panel, index) {
var imagecache_form = null;
}
/*
* Transcoding
*/
var transcodingEnabled = new Ext.form.Checkbox({
name: 'transcoding_enabled',
fieldLabel: 'Enabled'
});
var transcodingPanel = new Ext.form.FieldSet({
title: 'Transcoding',
width: 700,
autoHeight: true,
collapsible: true,
animCollapse: true,
items: [transcodingEnabled]
});
if (tvheadend.capabilities.indexOf('transcoding') === -1)
transcodingPanel.hide();
/* ****************************************************************
* Form
* ***************************************************************/
@ -252,7 +230,7 @@ tvheadend.miscconf = function(panel, index) {
layout: 'form',
defaultType: 'textfield',
autoHeight: true,
items: [languageWrap, dvbscanWrap, tvhtimePanel, transcodingPanel, piconPanel]
items: [languageWrap, dvbscanWrap, tvhtimePanel, piconPanel]
});
var _items = [confpanel];

View file

@ -17,6 +17,7 @@ tvheadend.esfilter_tab = function(panel)
clazz: 'profile',
comet: 'profile',
titleS: 'Stream Profile',
titleP: 'Stream Profiles',
titleC: 'Stream Profile Name',
iconCls: 'stream_profile',
add: {

View file

@ -37,7 +37,6 @@
#include "dvr/dvr.h"
#include "filebundle.h"
#include "streaming.h"
#include "plumbing/transcoding.h"
#include "profile.h"
#include "epg.h"
#include "muxer.h"
@ -82,63 +81,6 @@ is_client_simple(http_connection_t *hc)
return 0;
}
#if ENABLE_LIBAV
static int
http_get_transcoder_properties(struct http_arg_list *args,
transcoder_props_t *props)
{
int transcode;
const char *s;
memset(props, 0, sizeof(transcoder_props_t));
if ((s = http_arg_get(args, "transcode")))
transcode = atoi(s);
else
transcode = 0;
if ((s = http_arg_get(args, "resolution")))
props->tp_resolution = atoi(s);
else
props->tp_resolution = 384;
if ((s = http_arg_get(args, "channels")))
props->tp_channels = atoi(s);
else
props->tp_channels = 0; //same as source
if ((s = http_arg_get(args, "bandwidth")))
props->tp_bandwidth = atoi(s);
else
props->tp_bandwidth = 0; //same as source
if ((s = http_arg_get(args, "language")))
strncpy(props->tp_language, s, 3);
else
strncpy(props->tp_language, config_get_language() ?: "", 3);
if ((s = http_arg_get(args, "vcodec")))
props->tp_vcodec = streaming_component_txt2type(s);
else
props->tp_vcodec = SCT_UNKNOWN;
if ((s = http_arg_get(args, "acodec")))
props->tp_acodec = streaming_component_txt2type(s);
else
props->tp_acodec = SCT_UNKNOWN;
if ((s = http_arg_get(args, "scodec")))
props->tp_scodec = streaming_component_txt2type(s);
else
props->tp_scodec = SCT_UNKNOWN;
return transcode && transcoding_enabled;
}
#endif
/**
* Root page, we direct the client to different pages depending
* on if it is a full blown browser or just some mobile app
@ -449,26 +391,6 @@ http_channel_playlist(http_connection_t *hc, channel_t *channel)
htsbuf_qprintf(hq, "http://%s%s?ticket=%s", host, buf,
access_ticket_create(buf, hc->hc_access));
#if ENABLE_LIBAV
transcoder_props_t props;
if(http_get_transcoder_properties(&hc->hc_req_args, &props)) {
htsbuf_qprintf(hq, "&transcode=1");
if(props.tp_resolution)
htsbuf_qprintf(hq, "&resolution=%d", props.tp_resolution);
if(props.tp_channels)
htsbuf_qprintf(hq, "&channels=%d", props.tp_channels);
if(props.tp_bandwidth)
htsbuf_qprintf(hq, "&bandwidth=%d", props.tp_bandwidth);
if(props.tp_language[0])
htsbuf_qprintf(hq, "&language=%s", props.tp_language);
if(props.tp_vcodec)
htsbuf_qprintf(hq, "&vcodec=%s", streaming_component_type2txt(props.tp_vcodec));
if(props.tp_acodec)
htsbuf_qprintf(hq, "&acodec=%s", streaming_component_type2txt(props.tp_acodec));
if(props.tp_scodec)
htsbuf_qprintf(hq, "&scodec=%s", streaming_component_type2txt(props.tp_scodec));
}
#endif
htsbuf_qprintf(hq, "&profile=%s\n", profile);
http_output_content(hc, "audio/x-mpegurl");