From: Ukn Unknown <4031821+uknunknown@users.noreply.github.com> Date: Mon, 14 Apr 2025 04:09:12 +0000 (-0700) Subject: add mpegts parameters from input stream X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ede23be0d25fc7d42f144fec551e4742f082a37c;p=thirdparty%2Ftvheadend.git add mpegts parameters from input stream - add service_name, service_provider, mpegts_transport_id, mpegts_service_type, mpegts_pmt_start_pid, mpegts_start_pid, mpegts_service_id, mpegts_original_service_id - allow user to select mpeg ts sid (same like pass profile) --- diff --git a/src/muxer.h b/src/muxer.h index 7cf591f96..55a9a2947 100644 --- a/src/muxer.h +++ b/src/muxer.h @@ -83,6 +83,13 @@ typedef struct muxer_config { int m_killsig; int m_killtimeout; } pass; +#if ENABLE_LIBAV + struct { + uint16_t m_rewrite_sid; + int m_rewrite_pmt; + int m_rewrite_nit; + } transcode; +#endif struct { int m_dvbsub_reorder; } mkv; diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c index 79a4a969b..04e844143 100644 --- a/src/muxer/muxer_libav.c +++ b/src/muxer/muxer_libav.c @@ -356,6 +356,7 @@ lav_muxer_init(muxer_t* m, struct streaming_start *ss, const char *name) AVDictionary *opts = NULL; lav_muxer_t *lm = (lav_muxer_t*)m; char app[128]; + char mpegts_info[8]; #if LIBAVCODEC_VERSION_MAJOR > 58 const AVOutputFormat *fmt; #else @@ -396,15 +397,24 @@ lav_muxer_init(muxer_t* m, struct streaming_start *ss, const char *name) return -1; } av_dict_set(&oc->metadata, "title", name, 0); - av_dict_set(&oc->metadata, "service_name", name, 0); - av_dict_set(&oc->metadata, "service_provider", app, 0); + av_dict_set(&lm->lm_oc->metadata, "title", name, 0); + if (ss->ss_si.si_service){ + av_dict_set(&lm->lm_oc->metadata, "service_name", ss->ss_si.si_service, 0); + tvhdebug(LS_LIBAV, "service_name = %s", ss->ss_si.si_service); + } else + av_dict_set(&oc->metadata, "service_name", name, 0); + if (ss->ss_si.si_provider){ + av_dict_set(&lm->lm_oc->metadata, "service_provider", ss->ss_si.si_provider, 0); + tvhdebug(LS_LIBAV, "service_provider = %s", ss->ss_si.si_provider); + } else + av_dict_set(&oc->metadata, "service_provider", app, 0); lm->bsf_h264_filter = av_bsf_get_by_name("h264_mp4toannexb"); if (lm->bsf_h264_filter == NULL) { tvhwarn(LS_LIBAV, "Failed to get BSF: h264_mp4toannexb"); } lm->bsf_hevc_filter = av_bsf_get_by_name("hevc_mp4toannexb"); - if (lm->bsf_h264_filter == NULL) { + if (lm->bsf_hevc_filter == NULL) { tvhwarn(LS_LIBAV, "Failed to get BSF: hevc_mp4toannexb"); } lm->bsf_vp9_filter = av_bsf_get_by_name("vp9_superframe"); @@ -441,6 +451,61 @@ lav_muxer_init(muxer_t* m, struct streaming_start *ss, const char *name) av_dict_set(&opts, "ism_lookahead", "0", 0); } + if(lm->m_config.m_type == MC_MPEGTS) { + // parameters are required to make mpeg-ts output compliant with mpeg-ts standard + // implemented using documentation: https://ffmpeg.org/ffmpeg-formats.html#mpegts-1 + if (lm->m_config.u.transcode.m_rewrite_sid > 0) { + // override from profile requested by teh user + snprintf(mpegts_info, sizeof(mpegts_info), "0x%04x", lm->m_config.u.transcode.m_rewrite_sid); + tvhdebug(LS_LIBAV, "MPEGTS: mpegts_service_id = %s", mpegts_info); + av_dict_set(&opts, "mpegts_service_id", mpegts_info, 0); + // if override requested by the user we let ffmpeg default to decide (0x1000) + if (!lm->m_config.u.transcode.m_rewrite_pmt) { + snprintf(mpegts_info, sizeof(mpegts_info), "0x%04x", ss->ss_pmt_pid); + tvhdebug(LS_LIBAV, "MPEGTS: mpegts_pmt_start_pid = %s", mpegts_info); + av_dict_set(&opts, "mpegts_pmt_start_pid", mpegts_info, 0); + } + if (!lm->m_config.u.transcode.m_rewrite_nit) { + av_dict_set(&opts, "mpegts_flags", "nit", 0); + } + } + else { + // we transfer as many parameters as possible + if (ss->ss_si.si_tsid) { + snprintf(mpegts_info, sizeof(mpegts_info), "0x%04x", ss->ss_si.si_tsid); + tvhdebug(LS_LIBAV, "MPEGTS: mpegts_transport_stream_id = %s", mpegts_info); + av_dict_set(&opts, "mpegts_transport_stream_id", mpegts_info, 0); + } + if (ss->ss_si.si_type) { + snprintf(mpegts_info, sizeof(mpegts_info), "0x%04x", ss->ss_si.si_type); + tvhdebug(LS_LIBAV, "MPEGTS: mpegts_service_type = %s", mpegts_info); + av_dict_set(&opts, "mpegts_service_type", mpegts_info, 0); + } + if (ss->ss_pmt_pid) { + snprintf(mpegts_info, sizeof(mpegts_info), "0x%04x", ss->ss_pmt_pid); + tvhdebug(LS_LIBAV, "MPEGTS: mpegts_pmt_start_pid = %s", mpegts_info); + av_dict_set(&opts, "mpegts_pmt_start_pid", mpegts_info, 0); + } + if (ss->ss_pcr_pid) { + snprintf(mpegts_info, sizeof(mpegts_info), "0x%04x", ss->ss_pcr_pid); + tvhdebug(LS_LIBAV, "MPEGTS: mpegts_start_pid = %s", mpegts_info); + av_dict_set(&opts, "mpegts_start_pid", mpegts_info, 0); + } + if (ss->ss_service_id) { + snprintf(mpegts_info, sizeof(mpegts_info), "0x%04x", ss->ss_service_id); + tvhdebug(LS_LIBAV, "MPEGTS: mpegts_service_id = %s", mpegts_info); + av_dict_set(&opts, "mpegts_service_id", mpegts_info, 0); + } + if (ss->ss_si.si_onid) { + snprintf(mpegts_info, sizeof(mpegts_info), "0x%04x", ss->ss_si.si_onid); + tvhdebug(LS_LIBAV, "MPEGTS: mpegts_original_network_id = %s", mpegts_info); + av_dict_set(&opts, "mpegts_original_network_id", mpegts_info, 0); + } + av_dict_set(&opts, "mpegts_flags", "nit", 0); + av_dict_set(&opts, "mpegts_copyts", "1", 0); + } + } + if(!lm->lm_oc->nb_streams) { tvherror(LS_LIBAV, "No supported streams available"); lm->m_errors++; diff --git a/src/profile.c b/src/profile.c index 6fa5a4837..1aa9c57c8 100644 --- a/src/profile.c +++ b/src/profile.c @@ -2254,6 +2254,11 @@ profile_libav_mp4_builder(void) typedef struct profile_transcode { profile_t; int pro_mc; +#if ENABLE_LIBAV + uint16_t pro_rewrite_sid; + int pro_rewrite_pmt; + int pro_rewrite_nit; +#endif char *pro_vcodec; char *pro_src_vcodec; char *pro_acodec; @@ -2262,6 +2267,48 @@ typedef struct profile_transcode { char *pro_src_scodec; } profile_transcode_t; +#if ENABLE_LIBAV +static int +profile_transcode_rewrite_sid_set (void *in, const void *v) +{ + profile_transcode_t *pro = (profile_transcode_t *)in; + const uint16_t *val = v; + if (*val != pro->pro_rewrite_sid) { + if (*val > 0) { + pro->pro_rewrite_pmt = 1; + pro->pro_rewrite_nit = 1; + } + pro->pro_rewrite_sid = *val; + return 1; + } + return 0; +} + +static int +profile_transcode_int_set (void *in, const void *v, int *prop) +{ + profile_transcode_t *pro = (profile_transcode_t *)in; + int val = *(int *)v; + if (pro->pro_rewrite_sid > 0) val = 1; + if (val != *prop) { + *prop = val; + return 1; + } + return 0; +} + +static int +profile_transcode_rewrite_pmt_set (void *in, const void *v) +{ + return profile_transcode_int_set(in, v, &((profile_transcode_t *)in)->pro_rewrite_pmt); +} + +static int +profile_transcode_rewrite_nit_set (void *in, const void *v) +{ + return profile_transcode_int_set(in, v, &((profile_transcode_t *)in)->pro_rewrite_nit); +} +#endif static htsmsg_t * profile_class_mc_list ( void *o, const char *lang ) @@ -2470,6 +2517,12 @@ const idclass_t profile_transcode_class = .name = N_("Transcoding Settings"), .number = 2, }, +#if ENABLE_LIBAV + { + .name = N_("Rewrite MPEG-TS SI Table(s) Settings"), + .number = 3, + }, +#endif {} }, .ic_properties = (const property_t[]){ @@ -2484,6 +2537,51 @@ const idclass_t profile_transcode_class = .opts = PO_DOC_NLIST, .group = 1 }, +#if ENABLE_LIBAV + { + .type = PT_U16, + .id = "sid", + .name = N_("Rewrite Service ID"), + .desc = N_("Rewrite service identifier (SID) using the specified " + "value (usually 1). Zero means no rewrite; preserving " + "MPEG-TS original network and transport stream IDs"), + .off = offsetof(profile_transcode_t, pro_rewrite_sid), + .set = profile_transcode_rewrite_sid_set, + .opts = PO_EXPERT, + .def.i = 1, + .group = 3 + }, + { + .type = PT_BOOL, + .id = "rewrite_pmt", + .name = N_("Rewrite PMT"), + .desc = N_("Rewrite PMT (Program Map Table) packets to only " + "include information about the currently-streamed " + "service. " + "Rewrite can be unset only if 'Rewrite Service ID' " + "is set to zero."), + .off = offsetof(profile_transcode_t, pro_rewrite_pmt), + .set = profile_transcode_rewrite_pmt_set, + .opts = PO_EXPERT, + .def.i = 1, + .group = 3 + }, + { + .type = PT_BOOL, + .id = "rewrite_nit", + .name = N_("Rewrite NIT"), + .desc = N_("Rewrite NIT (Network Information Table) packets " + "to only include information about the currently-" + "streamed service. " + "Rewrite can be unset only if 'Rewrite Service ID' " + "is set to zero."), + .off = offsetof(profile_transcode_t, pro_rewrite_nit), + .set = profile_transcode_rewrite_nit_set, + .opts = PO_EXPERT, + .def.i = 1, + .group = 3 + }, +#endif { .type = PT_STR, .id = "pro_vcodec", @@ -2675,6 +2773,11 @@ profile_transcode_reopen(profile_chain_t *prch, if (!profile_transcode_mc_valid(c.m_type)) c.m_type = MC_MATROSKA; } + #if ENABLE_LIBAV + c.u.transcode.m_rewrite_sid = pro->pro_rewrite_sid; + c.u.transcode.m_rewrite_pmt = pro->pro_rewrite_pmt; + c.u.transcode.m_rewrite_nit = pro->pro_rewrite_nit; + #endif assert(!prch->prch_muxer); prch->prch_muxer = muxer_create(&c, hints); diff --git a/src/transcoding/transcode/transcoder.c b/src/transcoding/transcode/transcoder.c index 902f444aa..e19ccfff1 100644 --- a/src/transcoding/transcode/transcoder.c +++ b/src/transcoding/transcode/transcoder.c @@ -196,6 +196,7 @@ tvh_transcoder_start(TVHTranscoder *self, tvh_ss_t *ss_src) ss->ss_pcr_pid = ss_src->ss_pcr_pid; ss->ss_pmt_pid = ss_src->ss_pmt_pid; service_source_info_copy(&ss->ss_si, &ss_src->ss_si); + ss->ss_service_id = ss_src->ss_service_id; for (j = k = 0; j < count; j++) { i = indexes[j]; ssc_src = &ss_src->ss_components[i]; diff --git a/src/webui/static/app/profile.js b/src/webui/static/app/profile.js index b0ef7cec3..afcd38580 100644 --- a/src/webui/static/app/profile.js +++ b/src/webui/static/app/profile.js @@ -2,6 +2,38 @@ * Stream Profiles */ +let stream_profile_forms = { + 'default': function(form) { + let name_field = form.findField('name'); + name_field.maxLength = 31; // TVH_NAME_LEN -1 + }, + + 'profile-transcode': function(form) { + function updateMPEGTS(form) { + // enable settings for MPEG-TS/av-lib + // /src/muxer.h:32 MC_MPEGTS = 2, + if (form.findField('container').getValue() == 2) { + form.findField('sid').setDisabled(false); + form.findField('rewrite_pmt').setDisabled(false); + form.findField('rewrite_nit').setDisabled(false); + } + else { + form.findField('sid').setDisabled(true); + form.findField('rewrite_pmt').setDisabled(true); + form.findField('rewrite_nit').setDisabled(true); + } + } + + // update when form is loaded + updateMPEGTS(form); + + // on container change + form.findField('container').on('select', function(combo, record, index) { + updateMPEGTS(form); + }); + } +}; + tvheadend.profile_tab = function(panel) { if (!tvheadend.profile_builders) { @@ -38,6 +70,7 @@ tvheadend.profile_tab = function(panel) }, create: { } }, - del: true + del: true, + forms: stream_profile_forms }); };