From 07235bbf46100caab241fd707a135eccd3a923c4 Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Mon, 22 Apr 2019 22:11:29 +0200 Subject: [PATCH] mpegtsmux: Expose bitrate property This allows outputting a Transport Stream with a constant bitrate, by inserting null packets. --- gst/mpegtsmux/mpegtsmux.c | 19 +++++++- gst/mpegtsmux/mpegtsmux.h | 1 + gst/mpegtsmux/tsmux/tsmux.c | 79 ++++++++++++++++++++++++++++++- gst/mpegtsmux/tsmux/tsmux.h | 4 ++ gst/mpegtsmux/tsmux/tsmuxcommon.h | 2 + gst/mpegtsmux/tsmux/tsmuxstream.c | 2 + gst/mpegtsmux/tsmux/tsmuxstream.h | 2 + 7 files changed, 106 insertions(+), 3 deletions(-) diff --git a/gst/mpegtsmux/mpegtsmux.c b/gst/mpegtsmux/mpegtsmux.c index 58b1009bfa..6d145a6944 100644 --- a/gst/mpegtsmux/mpegtsmux.c +++ b/gst/mpegtsmux/mpegtsmux.c @@ -115,7 +115,8 @@ enum PROP_PAT_INTERVAL, PROP_PMT_INTERVAL, PROP_ALIGNMENT, - PROP_SI_INTERVAL + PROP_SI_INTERVAL, + PROP_BITRATE, }; #define MPEGTSMUX_DEFAULT_ALIGNMENT -1 @@ -305,6 +306,13 @@ mpegtsmux_class_init (MpegTsMuxClass * klass) "Set the interval (in ticks of the 90kHz clock) for writing out the Service" "Information tables", 1, G_MAXUINT, TSMUX_DEFAULT_SI_INTERVAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BITRATE, + g_param_spec_uint64 ("bitrate", "Bitrate (in bits per second)", + "Set the target bitrate, will insert null packets as padding " + " to achieve multiplex-wide constant bitrate", + 0, G_MAXUINT64, TSMUX_DEFAULT_BITRATE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void @@ -338,6 +346,7 @@ mpegtsmux_init (MpegTsMux * mux) mux->si_interval = TSMUX_DEFAULT_SI_INTERVAL; mux->prog_map = NULL; mux->alignment = MPEGTSMUX_DEFAULT_ALIGNMENT; + mux->bitrate = TSMUX_DEFAULT_BITRATE; /* initial state */ mpegtsmux_reset (mux, TRUE); @@ -511,6 +520,11 @@ gst_mpegtsmux_set_property (GObject * object, guint prop_id, mux->si_interval = g_value_get_uint (value); tsmux_set_si_interval (mux->tsmux, mux->si_interval); break; + case PROP_BITRATE: + mux->bitrate = g_value_get_uint64 (value); + if (mux->tsmux) + tsmux_set_bitrate (mux->tsmux, mux->bitrate); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -542,6 +556,9 @@ gst_mpegtsmux_get_property (GObject * object, guint prop_id, case PROP_SI_INTERVAL: g_value_set_uint (value, mux->si_interval); break; + case PROP_BITRATE: + g_value_set_uint64 (value, mux->bitrate); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/gst/mpegtsmux/mpegtsmux.h b/gst/mpegtsmux/mpegtsmux.h index 169892c79d..52381f6898 100644 --- a/gst/mpegtsmux/mpegtsmux.h +++ b/gst/mpegtsmux/mpegtsmux.h @@ -139,6 +139,7 @@ struct MpegTsMux { guint pmt_interval; gint alignment; guint si_interval; + guint64 bitrate; /* state */ gboolean first; diff --git a/gst/mpegtsmux/tsmux/tsmux.c b/gst/mpegtsmux/tsmux/tsmux.c index 0f09e4d440..e94c6808a2 100644 --- a/gst/mpegtsmux/tsmux/tsmux.c +++ b/gst/mpegtsmux/tsmux/tsmux.c @@ -634,6 +634,8 @@ tsmux_packet_out (TsMux * mux, GstBuffer * buf, gint64 pcr) return TRUE; } + mux->n_bytes += gst_buffer_get_size (buf); + return mux->write_func (buf, mux->write_func_data, pcr); } @@ -1019,6 +1021,67 @@ tsmux_write_si (TsMux * mux) } +static void +tsmux_write_null_ts_header (guint8 * buf) +{ + *buf++ = TSMUX_SYNC_BYTE; + *buf++ = 0x1f; + *buf++ = 0xff; + *buf++ = 0x10; +} + +static gboolean +pad_stream (TsMux * mux, TsMuxStream * stream, gint64 cur_ts) +{ + guint64 bitrate; + GstBuffer *buf = NULL; + GstMapInfo map; + gboolean ret = TRUE; + + do { + if (GST_CLOCK_STIME_IS_VALID (cur_ts)) { + GstClockTimeDiff diff; + + if (!GST_CLOCK_STIME_IS_VALID (stream->first_ts)) + stream->first_ts = cur_ts; + + diff = GST_CLOCK_DIFF (stream->first_ts, cur_ts); + + if (diff) { + bitrate = + gst_util_uint64_scale (mux->n_bytes * 8, TSMUX_CLOCK_FREQ, diff); + + GST_LOG ("Transport stream bitrate: %" G_GUINT64_FORMAT, bitrate); + + if (bitrate < mux->bitrate) { + GST_LOG_OBJECT (mux, "Padding transport stream"); + + if (!tsmux_get_buffer (mux, &buf)) { + ret = FALSE; + goto done; + } + + gst_buffer_map (buf, &map, GST_MAP_READ); + + tsmux_write_null_ts_header (map.data); + + gst_buffer_unmap (buf, &map); + + if (!(ret = tsmux_packet_out (mux, buf, -1))) + goto done; + } + } else { + break; + } + } else { + break; + } + } while (bitrate < mux->bitrate); + +done: + return ret; +} + /** * tsmux_write_stream_packet: * @mux: a #TsMux @@ -1052,6 +1115,11 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream) else cur_ts = tsmux_stream_get_pts (stream); + if (mux->bitrate) { + if (!pad_stream (mux, stream, cur_ts)) + goto fail; + } + cur_pcr = 0; if (cur_ts != G_MININT64) { TS_DEBUG ("TS for PCR stream is %" G_GINT64_FORMAT, cur_ts); @@ -1165,9 +1233,10 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream) /* ERRORS */ fail: { - gst_buffer_unmap (buf, &map); - if (buf) + if (buf) { + gst_buffer_unmap (buf, &map); gst_buffer_unref (buf); + } return FALSE; } } @@ -1312,3 +1381,9 @@ tsmux_write_pmt (TsMux * mux, TsMuxProgram * program) return tsmux_section_write_packet (GINT_TO_POINTER (GST_MPEGTS_SECTION_PMT), &program->pmt, mux); } + +void +tsmux_set_bitrate (TsMux * mux, guint64 bitrate) +{ + mux->bitrate = bitrate; +} diff --git a/gst/mpegtsmux/tsmux/tsmux.h b/gst/mpegtsmux/tsmux/tsmux.h index 11a1480bbf..a8da29b7f5 100644 --- a/gst/mpegtsmux/tsmux/tsmux.h +++ b/gst/mpegtsmux/tsmux/tsmux.h @@ -178,6 +178,9 @@ struct TsMux { /* scratch space for writing ES_info descriptors */ guint8 es_info_buf[TSMUX_MAX_ES_INFO_LENGTH]; + + guint64 bitrate; + guint64 n_bytes; }; /* create/free new muxer session */ @@ -191,6 +194,7 @@ void tsmux_set_pat_interval (TsMux *mux, guint interval); guint tsmux_get_pat_interval (TsMux *mux); void tsmux_resend_pat (TsMux *mux); guint16 tsmux_get_new_pid (TsMux *mux); +void tsmux_set_bitrate (TsMux *mux, guint64 bitrate); /* pid/program management */ TsMuxProgram * tsmux_program_new (TsMux *mux, gint prog_id); diff --git a/gst/mpegtsmux/tsmux/tsmuxcommon.h b/gst/mpegtsmux/tsmux/tsmuxcommon.h index 6d5e87fd58..7121a299ff 100644 --- a/gst/mpegtsmux/tsmux/tsmuxcommon.h +++ b/gst/mpegtsmux/tsmux/tsmuxcommon.h @@ -121,6 +121,8 @@ G_BEGIN_DECLS #define TSMUX_DEFAULT_PMT_INTERVAL (TSMUX_CLOCK_FREQ / 10) /* SI interval (1/10th sec) */ #define TSMUX_DEFAULT_SI_INTERVAL (TSMUX_CLOCK_FREQ / 10) +/* Bitrate (bits per second) */ +#define TSMUX_DEFAULT_BITRATE 0 typedef struct TsMuxPacketInfo TsMuxPacketInfo; typedef struct TsMuxProgram TsMuxProgram; diff --git a/gst/mpegtsmux/tsmux/tsmuxstream.c b/gst/mpegtsmux/tsmux/tsmuxstream.c index b6c975b0ea..7945fc0f91 100644 --- a/gst/mpegtsmux/tsmux/tsmuxstream.c +++ b/gst/mpegtsmux/tsmux/tsmuxstream.c @@ -224,6 +224,8 @@ tsmux_stream_new (guint16 pid, TsMuxStreamType stream_type) break; } + stream->first_ts = GST_CLOCK_STIME_NONE; + stream->last_pts = GST_CLOCK_STIME_NONE; stream->last_dts = GST_CLOCK_STIME_NONE; diff --git a/gst/mpegtsmux/tsmux/tsmuxstream.h b/gst/mpegtsmux/tsmux/tsmuxstream.h index 5234c489ea..3b160207e6 100644 --- a/gst/mpegtsmux/tsmux/tsmuxstream.h +++ b/gst/mpegtsmux/tsmux/tsmuxstream.h @@ -197,6 +197,8 @@ struct TsMuxStream { gint64 last_dts; gint64 last_pts; + gint64 first_ts; + /* count of programs using this as PCR */ gint pcr_ref; /* Next time PCR should be written */