Repository: tvheadend/tvheadend
Pull request #1814: add mpegts parameters from input stream
Author: login: uknunknown
Labels: name: IMPROVEMENT
name: READY-FOR-MERGE
Created: 2025-04-14T04:10:05Z | Merged: 2025-05-25T16:35:19Z

- 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


Diff:
diff --git a/src/muxer.h b/src/muxer.h
index 7cf591f96c..55a9a29471 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 79a4a969b9..04e8441435 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 6fa5a4837d..1aa9c57c80 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 902f444aad..e19ccfff1c 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 b0ef7cec3e..afcd385808 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
     });
 };


Reviews:
user: login: chewitt
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-14T04:21:44Z
user: login: uknunknown
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T18:17:07Z
user: login: chewitt
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-14T18:35:46Z
user: login: uknunknown
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T18:47:38Z
user: login: Flole998
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T13:27:53Z
user: login: Flole998
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T13:27:57Z
user: login: Flole998
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T14:46:57Z
user: login: uknunknown
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T23:26:01Z
user: login: Flole998
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-23T23:28:01Z
user: login: uknunknown
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T23:34:08Z
user: login: Flole998
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-25T11:13:41Z

Review comments:
diff_hunk: @@ -441,6 +451,42 @@ 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) {
+    // implement as many parameters from: https://ffmpeg.org/ffmpeg-formats.html#mpegts-1
path: src/muxer/muxer_libav.c
user: login: chewitt
body: the comment should describe the functional change and/or why they are included, not the act of implementation
created_at: 2025-04-14T04:21:44Z
author_association: MEMBER
diff_hunk: @@ -441,6 +451,42 @@ 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) {
+    // implement as many parameters from: https://ffmpeg.org/ffmpeg-formats.html#mpegts-1
path: src/muxer/muxer_libav.c
user: login: uknunknown
body: should I replace with?
`// implemented using documentation: https://ffmpeg.org/ffmpeg-formats.html#mpegts-1`
created_at: 2025-04-14T18:17:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -441,6 +451,42 @@ 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) {
+    // implement as many parameters from: https://ffmpeg.org/ffmpeg-formats.html#mpegts-1
path: src/muxer/muxer_libav.c
user: login: chewitt
body: better to describe why they are added or the purpose they serve
created_at: 2025-04-14T18:35:46Z
author_association: MEMBER
diff_hunk: @@ -441,6 +451,42 @@ 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) {
+    // implement as many parameters from: https://ffmpeg.org/ffmpeg-formats.html#mpegts-1
path: src/muxer/muxer_libav.c
user: login: uknunknown
body: done.
created_at: 2025-04-14T18:47:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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);
path: src/webui/static/app/profile.js
user: login: Flole998
body: Did you consider that these must be set? Simply disabling those will keep the current state, so they might get disabled in "Off" state?
created_at: 2025-05-23T13:27:53Z
author_association: MEMBER
diff_hunk: @@ -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' "
path: src/profile.c
user: login: Flole998
body: This sentence required quite some thinking about what it means. How about "Must  be set if ..... is non-zero"?
created_at: 2025-05-23T13:27:57Z
author_association: MEMBER
diff_hunk: @@ -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' "
path: src/profile.c
user: login: Flole998
body: Or instead of "non-zero" maybe call it "active" or "used", whatever fits best. The current double negation ist just more difficult to understand IMO.
created_at: 2025-05-23T14:46:57Z
author_association: MEMBER
diff_hunk: @@ -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);
path: src/webui/static/app/profile.js
user: login: uknunknown
body: The reason of "disable" is to show the user that those settings are not used if container is different than MPEG-TS/av-lib. doesn't matter if are enabled or disabled ... are not used. When MPEG-TS/av-lib is selected those components will become active (to be changed by the user).
Let me know if you disagree with this mind set.
![image](https://github.com/user-attachments/assets/78830ada-6b54-4d3c-ab7b-0147af909def)

![image](https://github.com/user-attachments/assets/05fbe7f3-21ed-488c-b4cf-00bc7b2ba6fe)

created_at: 2025-05-23T23:26:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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);
path: src/webui/static/app/profile.js
user: login: Flole998
body: Got it, I thought this was to force-enable them when "Rewrite service ID" is used.
created_at: 2025-05-23T23:28:01Z
author_association: MEMBER
diff_hunk: @@ -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' "
path: src/profile.c
user: login: uknunknown
body: frankly this is a copy/paste from pass profile:
![image](https://github.com/user-attachments/assets/79430326-c8bf-4c5c-a34f-9bb6355f9cb5)

If you want to change this one ... we should change both of them (to be in sync). Let me know your decision. 

created_at: 2025-05-23T23:34:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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' "
path: src/profile.c
user: login: Flole998
body: Okay, so rewording if we deem it necessary must be done at another place anyways, so lets call it out of scope for this PR and merge it as-is (just waiting for the other builds to run through).
created_at: 2025-05-25T11:13:40Z
author_association: MEMBER

PR comments:
user: login: chewitt
created_at: 2025-04-14T04:22:50Z
body: Please describe why these are being included. What problem does the change solve?
author_association: MEMBER
user: login: uknunknown
created_at: 2025-04-14T18:36:46Z
body: This submission is adding some parameters for mpegts container. Some tools are more strict than other when it comes to standards ... like discussed on https://tvheadend.org/d/8186-tvheadend-buildinstall-on-lubuntu-2004-64bit-with-igpu-8-transcoding/101.
Let me clarify what this PR does:
1. is transferring as many parameters as possible from input stream to mpeg-ts/av-lib container.
2. Some parameters cannot be transferred with the same value due to limited amount of commands available: https://ffmpeg.org/ffmpeg-formats.html#mpegts-1
3. A good example is: mpegts_pmt_start_pid and mpegts_start_pid that allows me to define the start value; and the rest of the streams will have id incremented by 1 --> is not a perfect situation but I did what I could with commands available.
4. some streams from Private data category (like Teletext) are dropped in transcoding process due to deprecation / non-compliance with DVB.
5. I didn't managed to transfer EIT ... we can target that for future improvement.
6. this PR is fixing also a bug in trascoding.c --> `ss->ss_service_id = ss_src->ss_service_id;` service_id was not copied from source to destination.

Overall the end results are presented in this side-by-side comparison:
![comparison](https://github.com/user-attachments/assets/11e74cc6-0650-48ef-b60a-412524849a3a)

author_association: CONTRIBUTOR
user: login: uknunknown
created_at: 2025-05-11T16:06:14Z
body: @Flole998 we need your help to asses what and if we should add a series of additions to this PR. There some improvements discussed in https://tvheadend.org/d/8186-tvheadend-buildinstall-on-lubuntu-2004-64bit-with-igpu-8-transcoding/131  
I can try to address 1. (because some users might use those default MPEG-TS parameters) in this PR. We can work on 2. later on (on a different PR).
Let me know if you agree to add 1. "Reset MPEG-TS SI parameters" to this PR (so I can start working on it).
author_association: CONTRIBUTOR
user: login: Flole998
created_at: 2025-05-11T16:16:48Z
body: > Reset MPEG-TS SI parameters

The idea is to basically have an option to disable the current behaviour (which does what?) and "start from 0"? Could you explain on a high level what that change should do (like "PID is currently set to the input PID, the change would set it to xyz")? It feels like there should be individual options to override the changes (like "Output PID, -1 to copy the input PID/leave it unchanged").
author_association: MEMBER
user: login: uknunknown
created_at: 2025-05-11T16:35:14Z
body: My initial idea was to reset to 'some default value'; but your idea to copy the functionality from profile pass is much better/flexible. ffmpeg will not allow me to change all the parameters from pass :( ... because some don't have a ffmpeg implementation/command ... but I can add as many I can :) .
We can keep the same override implementation from pass profile - for consistency.
https://github.com/tvheadend/tvheadend/blob/master/src/profile.c#L1364
 `      .desc     = N_("Rewrite service identifier (SID) using the specified "
                     "value (usually 1). Zero means no rewrite."),`
author_association: CONTRIBUTOR
user: login: uknunknown
created_at: 2025-05-11T20:14:45Z
body: I found the following correspondence: 
tvh-pass  --> ffmpeg
"Rewrite Service ID"  --> "mpegts_service_id"
"Rewrite PMT" --> "mpegts_pmt_start_pid"
"Rewrite PAT" 
"Rewrite SDT"
"Rewrite NIT" --> "nit"
"Rewrite EIT"

If you are OK dropping "Rewrite PAT" , "Rewrite SDT" and "Rewrite EIT" --> I can start implementing the other ones.

author_association: CONTRIBUTOR
user: login: Flole998
created_at: 2025-05-11T20:34:11Z
body: I'm not entirely sure about the functionality of those features. I believe they rewrite the tables to contain the new service ID?

This wouldn't affect the pass profile in any way, right? So all you would do is add additional options to different profile types, right? 
author_association: MEMBER
user: login: uknunknown
created_at: 2025-05-11T21:06:26Z
body: >  I believe they rewrite the tables to contain the new service ID?

Yes

> This wouldn't affect the pass profile in any way, right? So all you would do is add additional options to different profile types, right?

That is correct ... actually will be added only to profile_transcode_class profile: https://github.com/tvheadend/tvheadend/blob/master/src/profile.c#L2475 (under Container).
Also I will try to disable by default (from javascript) those fields and enable those only when MPEG-TS/av-lib (as Container) is selected.
author_association: CONTRIBUTOR
user: login: uknunknown
created_at: 2025-05-12T01:39:43Z
body: @Flole998 during my implementation I found that: https://github.com/tvheadend/tvheadend/blob/master/src/profile.c#L1760
left is `int` and right is `uint16_t` and we don't have an explicit cast.
Do you think is a problem? Compiler does the job of casting automatically? 
author_association: CONTRIBUTOR
user: login: Flole998
created_at: 2025-05-12T07:00:02Z
body: > @Flole998 during my implementation I found that: https://github.com/tvheadend/tvheadend/blob/master/src/profile.c#L1760 left is `int` and right is `uint16_t` and we don't have an explicit cast. Do you think is a problem? Compiler does the job of casting automatically?

I can't find it in the line you linked
author_association: MEMBER
user: login: uknunknown
created_at: 2025-05-12T15:40:31Z
body: Or we should change the union to replace (int) with (uint16_t):
![int_uint16_t](https://github.com/user-attachments/assets/48c57b95-0a4e-435a-afb6-1c7413b3c6ed)

author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-05-13T03:32:35Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=tvheadend_tvheadend&pullRequest=1814) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=tvheadend_tvheadend&pullRequest=1814&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=tvheadend_tvheadend&pullRequest=1814&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tvheadend_tvheadend&pullRequest=1814&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=tvheadend_tvheadend&pullRequest=1814&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=tvheadend_tvheadend&pullRequest=1814&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=tvheadend_tvheadend&pullRequest=1814)


author_association: NONE
user: login: uknunknown
created_at: 2025-05-13T04:45:28Z
body: @Flole998 please take a look at the issues highlighted by sonar ... let me know if you think requires more work.
author_association: CONTRIBUTOR