mpegtsmux: Expose bitrate property

This allows outputting a Transport Stream with a constant bitrate,
by inserting null packets.
This commit is contained in:
Mathieu Duponchelle 2019-04-22 22:11:29 +02:00 committed by Mathieu Duponchelle
parent 4d53a7ac09
commit 07235bbf46
7 changed files with 106 additions and 3 deletions

View file

@ -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;

View file

@ -139,6 +139,7 @@ struct MpegTsMux {
guint pmt_interval;
gint alignment;
guint si_interval;
guint64 bitrate;
/* state */
gboolean first;

View file

@ -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:
{
if (buf) {
gst_buffer_unmap (buf, &map);
if (buf)
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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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 */