profile_t;
int pro_mc;
char *pro_vcodec;
+ char *pro_src_vcodec;
char *pro_acodec;
+ char *pro_src_acodec;
char *pro_scodec;
+ char *pro_src_scodec;
} profile_transcode_t;
return profile_class_codec_profiles_list(AVMEDIA_TYPE_VIDEO, lang);
}
+static htsmsg_t *
+profile_class_src_vcodec_list ( void *o, const char *lang )
+{
+ static const struct strtab_str tab[] = {
+ { "-", "" },
+ { "MPEG2VIDEO", "MPEG2VIDEO" },
+ { "H264", "H264" },
+ { "VP8", "VP8" },
+ { "HEVC", "HEVC" },
+ { "VP9", "VP9" },
+ { "THEORA", "THEORA" },
+ };
+ return strtab2htsmsg_str(tab, 1, lang);
+}
+
static htsmsg_t *
profile_class_pro_acodec_list(void *o, const char *lang)
{
return profile_class_codec_profiles_list(AVMEDIA_TYPE_AUDIO, lang);
}
+static htsmsg_t *
+profile_class_src_acodec_list ( void *o, const char *lang )
+{
+ static const struct strtab_str tab[] = {
+ { "-", "" },
+ { "MPEG2AUDIO", "MPEG2AUDIO" },
+ { "AC3", "AC3" },
+ { "AAC", "AAC" },
+ { "MP4A", "MP4A" },
+ { "EAC3", "EAC3" },
+ { "VORBIS", "VORBIS" },
+ { "OPUS", "OPUS" },
+ };
+ return strtab2htsmsg_str(tab, 1, lang);
+}
+
static htsmsg_t *
profile_class_pro_scodec_list(void *o, const char *lang)
{
return profile_class_codec_profiles_list(AVMEDIA_TYPE_SUBTITLE, lang);
}
+static htsmsg_t *
+profile_class_src_scodec_list ( void *o, const char *lang )
+{
+ static const struct strtab_str tab[] = {
+ { "-", "" },
+ { "DVBSUB", "DVBSUB" },
+ { "TEXTSUB", "TEXTSUB" },
+ };
+ return strtab2htsmsg_str(tab, 1, lang);
+}
+
const idclass_t profile_transcode_class =
{
.ic_super = &profile_class,
.opts = PO_ADVANCED,
.group = 2
},
+ {
+ .type = PT_STR,
+ .id = "src_vcodec",
+ .name = N_("Source video codec"),
+ .desc = N_("Transcode video only if source video codec match. "
+ "Empty or minus string will ignore source video codec "
+ "check and continue with transcode. Separate codec names "
+ "with coma. If no codec match found - transcode with "
+ "\"copy\" codec, if match found - transcode with video "
+ "codec defined in this profile."),
+ .off = offsetof(profile_transcode_t, pro_src_vcodec),
+ .list = profile_class_src_vcodec_list,
+ .opts = PO_ADVANCED,
+ .group = 2
+ },
{
.type = PT_STR,
.id = "pro_acodec",
.opts = PO_ADVANCED,
.group = 2
},
+ {
+ .type = PT_STR,
+ .id = "src_acodec",
+ .name = N_("Source audio codec"),
+ .desc = N_("Transcode audio only if source audio codec match. "
+ "Empty or minus string will ignore source audio codec "
+ "check and continue with transcode. Separate codec names "
+ "with coma. If no codec match found - transcode with "
+ "\"copy\" codec, if match found - transcode with audio "
+ "codec defined in this profile."),
+ .off = offsetof(profile_transcode_t, pro_src_acodec),
+ .list = profile_class_src_acodec_list,
+ .opts = PO_ADVANCED,
+ .group = 2
+ },
{
.type = PT_STR,
.id = "pro_scodec",
.opts = PO_ADVANCED,
.group = 2
},
+ {
+ .type = PT_STR,
+ .id = "src_scodec",
+ .name = N_("Source subtitle codec"),
+ .desc = N_("Transcode subtitle only if source subtitle codec match. "
+ "Empty or minus string will ignore source subtitle codec "
+ "check and continue with transcode. Separate codec names "
+ "with coma. If no codec match found - transcode with "
+ "\"copy\" codec, if match found - transcode with subtitle "
+ "codec defined in this profile."),
+ .off = offsetof(profile_transcode_t, pro_src_acodec),
+ .list = profile_class_src_scodec_list,
+ .opts = PO_ADVANCED,
+ .group = 2
+ },
{ }
}
};
*/
if (strcmp(pro1->pro_vcodec ?: "", pro2->pro_vcodec ?: ""))
return 0;
+ if (strcmp(pro1->pro_src_vcodec ?: "", pro2->pro_src_vcodec ?: ""))
+ return 0;
if (strcmp(pro1->pro_acodec ?: "", pro2->pro_acodec ?: ""))
return 0;
+ if (strcmp(pro1->pro_src_acodec ?: "", pro2->pro_src_acodec ?: ""))
+ return 0;
if (strcmp(pro1->pro_scodec ?: "", pro2->pro_scodec ?: ""))
return 0;
+ if (strcmp(pro1->pro_src_scodec ?: "", pro2->pro_src_scodec ?: ""))
+ return 0;
return 1;
}
profile_sharer_t *prsh;
profile_transcode_t *pro = (profile_transcode_t *)prch->prch_pro;
const char *profiles[AVMEDIA_TYPE_NB] = { NULL };
+ const char *src_codecs[AVMEDIA_TYPE_NB] = { NULL };
prsh = profile_sharer_find(prch);
if (!prsh)
profiles[AVMEDIA_TYPE_AUDIO] = pro->pro_acodec ?: "";
profiles[AVMEDIA_TYPE_SUBTITLE] = pro->pro_scodec ?: "";
+ src_codecs[AVMEDIA_TYPE_VIDEO] = pro->pro_src_vcodec ?: "";
+ src_codecs[AVMEDIA_TYPE_AUDIO] = pro->pro_src_acodec ?: "";
+ src_codecs[AVMEDIA_TYPE_SUBTITLE] = pro->pro_src_scodec ?: "";
+
dst = prch->prch_gh = globalheaders_create(dst);
#if ENABLE_TIMESHIFT
goto fail;
if (!prsh->prsh_transcoder) {
assert(!prsh->prsh_tsfix);
- dst = prsh->prsh_transcoder = transcoder_create(&prsh->prsh_input, profiles);
+ dst = prsh->prsh_transcoder = transcoder_create(&prsh->prsh_input,
+ profiles, src_codecs);
if (!dst)
goto fail;
prsh->prsh_tsfix = tsfix_create(dst);
/* TVHTranscoder ============================================================ */
streaming_target_t *
-transcoder_create(streaming_target_t *output, const char **profiles);
+transcoder_create(streaming_target_t *output,
+ const char **profiles,
+ const char **src_codecs);
void
transcoder_destroy(streaming_target_t *st);
uint32_t id;
tvh_st_t *output;
TVHCodecProfile *profiles[AVMEDIA_TYPE_NB];
+ char *src_codecs[AVMEDIA_TYPE_NB];
} TVHTranscoder;
int
tvh_transcoder_deliver(TVHTranscoder *self, th_pkt_t *pkt);
TVHTranscoder *
-tvh_transcoder_create(tvh_st_t *output, const char **profiles);
+tvh_transcoder_create(tvh_st_t *output,
+ const char **profiles,
+ const char **src_codecs);
void
tvh_transcoder_destroy(TVHTranscoder *self);
TVHStream *
tvh_stream_create(TVHTranscoder *transcoder, TVHCodecProfile *profile,
- tvh_ssc_t *ssc);
+ tvh_ssc_t *ssc, const char *src_codecs);
void
tvh_stream_destroy(TVHStream *self);
/* TVHTranscoder ============================================================ */
streaming_target_t *
-transcoder_create(streaming_target_t *output, const char **profiles)
+transcoder_create(streaming_target_t *output,
+ const char **profiles,
+ const char **src_profiles)
{
- return (streaming_target_t *)tvh_transcoder_create(output, profiles);
+ return (streaming_target_t *)tvh_transcoder_create(output, profiles, src_profiles);
}
/* TVHStream ================================================================ */
static int
-tvh_stream_is_copy(TVHCodecProfile *profile, tvh_ssc_t *ssc)
+tvh_stream_is_copy(TVHCodecProfile *profile, tvh_ssc_t *ssc,
+ const char *src_codecs)
{
+ const char *txtname;
+ char *codecs, *str, *token, *saveptr;
+
+ /* if the source codec is in the list, do the stream copy only */
+ if (src_codecs && *src_codecs != '\0' && *src_codecs != '-') {
+ txtname = streaming_component_type2txt(ssc->ssc_type);
+ if (txtname == NULL)
+ goto cont;
+ codecs = tvh_strdupa(src_codecs);
+ if (codecs == NULL)
+ goto cont;
+ for (str = codecs; ; str = NULL) {
+ token = strtok_r(str," ,|;" , &saveptr);
+ if (token == NULL)
+ break;
+ if (!strcasecmp(token, txtname))
+ return 0; /* copy */
+ }
+ }
+cont:
if (profile == tvh_codec_profile_copy) {
return 1;
}
TVHStream *
tvh_stream_create(TVHTranscoder *transcoder, TVHCodecProfile *profile,
- tvh_ssc_t *ssc)
+ tvh_ssc_t *ssc, const char *src_codecs)
{
TVHStream *self = NULL;
int is_copy = -1;
self->transcoder = transcoder;
self->id = self->index = ssc->ssc_index;
self->type = ssc->ssc_type;
- if ((is_copy = tvh_stream_is_copy(profile, ssc)) > 0) {
+ if ((is_copy = tvh_stream_is_copy(profile, ssc, src_codecs)) > 0) {
if (ssc->ssc_gh) {
pktbuf_ref_inc(ssc->ssc_gh);
}
static enum AVMediaType
ssc_get_media_type(tvh_ssc_t *ssc)
{
+ if (ssc->ssc_disabled) {
+ return AVMEDIA_TYPE_UNKNOWN;
+ }
if (SCT_ISVIDEO(ssc->ssc_type)) {
return AVMEDIA_TYPE_VIDEO;
}
}
-static TVHCodecProfile *
-ssc_is_stream(tvh_ssc_t *ssc, TVHCodecProfile **profiles)
-{
- enum AVMediaType media_type = AVMEDIA_TYPE_UNKNOWN;
-
- if (!ssc->ssc_disabled &&
- (media_type = ssc_get_media_type(ssc)) != AVMEDIA_TYPE_UNKNOWN) {
- return profiles[media_type];
- }
- return NULL;
-}
-
-
static int
stream_count(tvh_ss_t *ss, TVHCodecProfile **profiles)
{
tvh_ssc_t *ssc = NULL;
for (i = 0; i < ss->ss_num_components; i++) {
- if ((ssc = &ss->ss_components[i]) && ssc_is_stream(ssc, profiles)) {
+ if ((ssc = &ss->ss_components[i]) &&
+ ssc_get_media_type(ssc) != AVMEDIA_TYPE_UNKNOWN) {
count++;
}
}
TVHCodecProfile *profile = NULL;
TVHStream *stream = NULL;
int i, j, count = stream_count(ss_src, self->profiles);
+ enum AVMediaType media_type;
ss = calloc(1, (sizeof(tvh_ss_t) + (sizeof(tvh_ssc_t) * count)));
if (ss) {
ssc_src = &ss_src->ss_components[i];
ssc = &ss->ss_components[j];
if (ssc) {
- if ((profile = ssc_is_stream(ssc_src, self->profiles))) {
+ media_type = ssc_get_media_type(ssc_src);
+ if (media_type != AVMEDIA_TYPE_UNKNOWN) {
+ profile = self->profiles[media_type];
*ssc = *ssc_src;
j++;
- if ((stream = tvh_stream_create(self, profile, ssc))) {
+ if ((stream = tvh_stream_create(self, profile, ssc,
+ self->src_codecs[media_type]))) {
SLIST_INSERT_HEAD(&self->streams, stream, link);
}
else {
static int
-tvh_transcoder_setup(TVHTranscoder *self, const char **profiles)
+tvh_transcoder_setup(TVHTranscoder *self,
+ const char **profiles,
+ const char **src_codecs)
{
const char *profile = NULL;
int i;
"failed to find codec profile: '%s'", profile);
return -1;
}
+ if (src_codecs[i])
+ self->src_codecs[i] = strdup(src_codecs[i]);
}
}
return 0;
TVHTranscoder *
-tvh_transcoder_create(tvh_st_t *output, const char **profiles)
+tvh_transcoder_create(tvh_st_t *output,
+ const char **profiles,
+ const char **src_codecs)
{
static uint32_t id = 0;
TVHTranscoder *self = NULL;
self->id = ++id;
}
self->output = output;
- if (tvh_transcoder_setup(self, profiles)) {
+ if (tvh_transcoder_setup(self, profiles, src_codecs)) {
tvh_transcoder_destroy(self);
return NULL;
}
tvh_transcoder_destroy(TVHTranscoder *self)
{
TVHStream *stream = NULL;
+ int i;
if (self) {
tvh_transcoder_stop(self, 0);
SLIST_REMOVE_HEAD(&self->streams, link);
tvh_stream_destroy(stream);
}
+ for (i = 0; i < AVMEDIA_TYPE_NB; i++)
+ free(self->src_codecs[i]);
free(self);
self = NULL;
}