]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
add mpegts parameters from input stream
authorUkn Unknown <4031821+uknunknown@users.noreply.github.com>
Mon, 14 Apr 2025 04:09:12 +0000 (21:09 -0700)
committerFlole <Flole998@users.noreply.github.com>
Sun, 25 May 2025 16:35:19 +0000 (18:35 +0200)
- 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)

src/muxer.h
src/muxer/muxer_libav.c
src/profile.c
src/transcoding/transcode/transcoder.c
src/webui/static/app/profile.js

index 7cf591f96c6a2b0a0f48c9aee80df41d4afe19bf..55a9a294714ac79d0c0b7c10909137114ca02fc6 100644 (file)
@@ -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;
index 79a4a969b913950cb5f2f533f761d3d35b24e85f..04e8441435331aaa56ac541c45c7b518a86f2dad 100644 (file)
@@ -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++;
index 6fa5a4837de9ac973024167f0e9e28d1d9f94747..1aa9c57c809a034131110bbfb8e4623f3051e221 100644 (file)
@@ -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);
index 902f444aad3480c14495770307ab594971c2e855..e19ccfff1c4ba4aa1cae740a248a8d1e499518ea 100644 (file)
@@ -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];
index b0ef7cec3eb28ebaae7e478f655e5e1bd35528d8..afcd385808dd4d0569c5990c1a8e97757558d3a9 100644 (file)
@@ -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
     });
 };