mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 11:45:25 +00:00
mpegtsmux: Expose bitrate property
This allows outputting a Transport Stream with a constant bitrate, by inserting null packets.
This commit is contained in:
parent
4d53a7ac09
commit
07235bbf46
7 changed files with 106 additions and 3 deletions
|
@ -115,7 +115,8 @@ enum
|
||||||
PROP_PAT_INTERVAL,
|
PROP_PAT_INTERVAL,
|
||||||
PROP_PMT_INTERVAL,
|
PROP_PMT_INTERVAL,
|
||||||
PROP_ALIGNMENT,
|
PROP_ALIGNMENT,
|
||||||
PROP_SI_INTERVAL
|
PROP_SI_INTERVAL,
|
||||||
|
PROP_BITRATE,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MPEGTSMUX_DEFAULT_ALIGNMENT -1
|
#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"
|
"Set the interval (in ticks of the 90kHz clock) for writing out the Service"
|
||||||
"Information tables", 1, G_MAXUINT, TSMUX_DEFAULT_SI_INTERVAL,
|
"Information tables", 1, G_MAXUINT, TSMUX_DEFAULT_SI_INTERVAL,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
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
|
static void
|
||||||
|
@ -338,6 +346,7 @@ mpegtsmux_init (MpegTsMux * mux)
|
||||||
mux->si_interval = TSMUX_DEFAULT_SI_INTERVAL;
|
mux->si_interval = TSMUX_DEFAULT_SI_INTERVAL;
|
||||||
mux->prog_map = NULL;
|
mux->prog_map = NULL;
|
||||||
mux->alignment = MPEGTSMUX_DEFAULT_ALIGNMENT;
|
mux->alignment = MPEGTSMUX_DEFAULT_ALIGNMENT;
|
||||||
|
mux->bitrate = TSMUX_DEFAULT_BITRATE;
|
||||||
|
|
||||||
/* initial state */
|
/* initial state */
|
||||||
mpegtsmux_reset (mux, TRUE);
|
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);
|
mux->si_interval = g_value_get_uint (value);
|
||||||
tsmux_set_si_interval (mux->tsmux, mux->si_interval);
|
tsmux_set_si_interval (mux->tsmux, mux->si_interval);
|
||||||
break;
|
break;
|
||||||
|
case PROP_BITRATE:
|
||||||
|
mux->bitrate = g_value_get_uint64 (value);
|
||||||
|
if (mux->tsmux)
|
||||||
|
tsmux_set_bitrate (mux->tsmux, mux->bitrate);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -542,6 +556,9 @@ gst_mpegtsmux_get_property (GObject * object, guint prop_id,
|
||||||
case PROP_SI_INTERVAL:
|
case PROP_SI_INTERVAL:
|
||||||
g_value_set_uint (value, mux->si_interval);
|
g_value_set_uint (value, mux->si_interval);
|
||||||
break;
|
break;
|
||||||
|
case PROP_BITRATE:
|
||||||
|
g_value_set_uint64 (value, mux->bitrate);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -139,6 +139,7 @@ struct MpegTsMux {
|
||||||
guint pmt_interval;
|
guint pmt_interval;
|
||||||
gint alignment;
|
gint alignment;
|
||||||
guint si_interval;
|
guint si_interval;
|
||||||
|
guint64 bitrate;
|
||||||
|
|
||||||
/* state */
|
/* state */
|
||||||
gboolean first;
|
gboolean first;
|
||||||
|
|
|
@ -634,6 +634,8 @@ tsmux_packet_out (TsMux * mux, GstBuffer * buf, gint64 pcr)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mux->n_bytes += gst_buffer_get_size (buf);
|
||||||
|
|
||||||
return mux->write_func (buf, mux->write_func_data, pcr);
|
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:
|
* tsmux_write_stream_packet:
|
||||||
* @mux: a #TsMux
|
* @mux: a #TsMux
|
||||||
|
@ -1052,6 +1115,11 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
|
||||||
else
|
else
|
||||||
cur_ts = tsmux_stream_get_pts (stream);
|
cur_ts = tsmux_stream_get_pts (stream);
|
||||||
|
|
||||||
|
if (mux->bitrate) {
|
||||||
|
if (!pad_stream (mux, stream, cur_ts))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
cur_pcr = 0;
|
cur_pcr = 0;
|
||||||
if (cur_ts != G_MININT64) {
|
if (cur_ts != G_MININT64) {
|
||||||
TS_DEBUG ("TS for PCR stream is %" G_GINT64_FORMAT, cur_ts);
|
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 */
|
/* ERRORS */
|
||||||
fail:
|
fail:
|
||||||
{
|
{
|
||||||
|
if (buf) {
|
||||||
gst_buffer_unmap (buf, &map);
|
gst_buffer_unmap (buf, &map);
|
||||||
if (buf)
|
|
||||||
gst_buffer_unref (buf);
|
gst_buffer_unref (buf);
|
||||||
|
}
|
||||||
return FALSE;
|
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),
|
return tsmux_section_write_packet (GINT_TO_POINTER (GST_MPEGTS_SECTION_PMT),
|
||||||
&program->pmt, mux);
|
&program->pmt, mux);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tsmux_set_bitrate (TsMux * mux, guint64 bitrate)
|
||||||
|
{
|
||||||
|
mux->bitrate = bitrate;
|
||||||
|
}
|
||||||
|
|
|
@ -178,6 +178,9 @@ struct TsMux {
|
||||||
|
|
||||||
/* scratch space for writing ES_info descriptors */
|
/* scratch space for writing ES_info descriptors */
|
||||||
guint8 es_info_buf[TSMUX_MAX_ES_INFO_LENGTH];
|
guint8 es_info_buf[TSMUX_MAX_ES_INFO_LENGTH];
|
||||||
|
|
||||||
|
guint64 bitrate;
|
||||||
|
guint64 n_bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* create/free new muxer session */
|
/* 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);
|
guint tsmux_get_pat_interval (TsMux *mux);
|
||||||
void tsmux_resend_pat (TsMux *mux);
|
void tsmux_resend_pat (TsMux *mux);
|
||||||
guint16 tsmux_get_new_pid (TsMux *mux);
|
guint16 tsmux_get_new_pid (TsMux *mux);
|
||||||
|
void tsmux_set_bitrate (TsMux *mux, guint64 bitrate);
|
||||||
|
|
||||||
/* pid/program management */
|
/* pid/program management */
|
||||||
TsMuxProgram * tsmux_program_new (TsMux *mux, gint prog_id);
|
TsMuxProgram * tsmux_program_new (TsMux *mux, gint prog_id);
|
||||||
|
|
|
@ -121,6 +121,8 @@ G_BEGIN_DECLS
|
||||||
#define TSMUX_DEFAULT_PMT_INTERVAL (TSMUX_CLOCK_FREQ / 10)
|
#define TSMUX_DEFAULT_PMT_INTERVAL (TSMUX_CLOCK_FREQ / 10)
|
||||||
/* SI interval (1/10th sec) */
|
/* SI interval (1/10th sec) */
|
||||||
#define TSMUX_DEFAULT_SI_INTERVAL (TSMUX_CLOCK_FREQ / 10)
|
#define TSMUX_DEFAULT_SI_INTERVAL (TSMUX_CLOCK_FREQ / 10)
|
||||||
|
/* Bitrate (bits per second) */
|
||||||
|
#define TSMUX_DEFAULT_BITRATE 0
|
||||||
|
|
||||||
typedef struct TsMuxPacketInfo TsMuxPacketInfo;
|
typedef struct TsMuxPacketInfo TsMuxPacketInfo;
|
||||||
typedef struct TsMuxProgram TsMuxProgram;
|
typedef struct TsMuxProgram TsMuxProgram;
|
||||||
|
|
|
@ -224,6 +224,8 @@ tsmux_stream_new (guint16 pid, TsMuxStreamType stream_type)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stream->first_ts = GST_CLOCK_STIME_NONE;
|
||||||
|
|
||||||
stream->last_pts = GST_CLOCK_STIME_NONE;
|
stream->last_pts = GST_CLOCK_STIME_NONE;
|
||||||
stream->last_dts = GST_CLOCK_STIME_NONE;
|
stream->last_dts = GST_CLOCK_STIME_NONE;
|
||||||
|
|
||||||
|
|
|
@ -197,6 +197,8 @@ struct TsMuxStream {
|
||||||
gint64 last_dts;
|
gint64 last_dts;
|
||||||
gint64 last_pts;
|
gint64 last_pts;
|
||||||
|
|
||||||
|
gint64 first_ts;
|
||||||
|
|
||||||
/* count of programs using this as PCR */
|
/* count of programs using this as PCR */
|
||||||
gint pcr_ref;
|
gint pcr_ref;
|
||||||
/* Next time PCR should be written */
|
/* Next time PCR should be written */
|
||||||
|
|
Loading…
Reference in a new issue