mpegtsmux: allow attaching PCR to non-PES streams

There is an existing PMT mapping between PCR_%s and an mpegtsmux sink
pad name, where %s equals the program number that the PCR corresponds
to. We re-purpose this functionality to also support a mapping between
PCR_%s and an arbitrary PID. If this mapping is set, then the header PCR
PID is set to this value, and PCR is attached to the stream with this
PID.

Note: the current implementation also attaches PCR to the video stream,
so this may be inefficient.

Co-authored-by: Jordan Yelloz <jordan.yelloz@collabora.com>
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5726>
This commit is contained in:
Aaron Boxer 2023-06-15 22:14:02 -04:00 committed by GStreamer Marge Bot
parent 75d1b3f92f
commit b405ff34d3
4 changed files with 124 additions and 13 deletions

View file

@ -876,6 +876,32 @@ gst_base_ts_mux_create_stream (GstBaseTsMux * mux, GstBaseTsMuxPad * ts_pad)
return ret;
}
static guint16
get_pmt_pcr_pid (GstBaseTsMux * mux, const gchar * prop_name)
{
if (mux->prog_map == NULL)
return 0;
gint pcr_pid = 0;
if (!gst_structure_get (mux->prog_map, prop_name, G_TYPE_INT, &pcr_pid, NULL))
return 0;
if (pcr_pid < 1 || pcr_pid > G_MAXUINT16)
return 0;
return (guint16) pcr_pid;
}
static gchar *
get_pmt_pcr_sink (GstBaseTsMux * mux, const gchar * prop_name)
{
if (mux->prog_map == NULL)
return 0;
gchar *pcr_sink = NULL;
if (!gst_structure_get (mux->prog_map, prop_name, G_TYPE_STRING, &pcr_sink,
NULL)) {
return NULL;
}
return pcr_sink;
}
/* Must be called with mux->lock held */
static GstFlowReturn
gst_base_ts_mux_create_pad_stream (GstBaseTsMux * mux, GstPad * pad)
@ -945,6 +971,7 @@ gst_base_ts_mux_create_pad_stream (GstBaseTsMux * mux, GstPad * pad)
if (ret != GST_FLOW_OK)
goto no_stream;
}
ts_pad->stream->program = ts_pad->prog;
if (ts_pad->prog->pcr_stream == NULL) {
/* Take the first stream of the program for the PCR */
@ -957,17 +984,23 @@ gst_base_ts_mux_create_pad_stream (GstBaseTsMux * mux, GstPad * pad)
/* Check for user-specified PCR PID */
prop_name = g_strdup_printf ("PCR_%d", ts_pad->prog->pgm_number);
if (mux->prog_map && gst_structure_has_field (mux->prog_map, prop_name)) {
const gchar *sink_name =
gst_structure_get_string (mux->prog_map, prop_name);
if (!g_strcmp0 (name, sink_name)) {
GST_DEBUG_OBJECT (mux, "User specified stream (pid=%d) as PCR for "
"program (prog_id = %d)", ts_pad->pid, ts_pad->prog->pgm_number);
tsmux_program_set_pcr_stream (ts_pad->prog, ts_pad->stream);
}
guint16 pcr_pid = get_pmt_pcr_pid (mux, prop_name);
if (pcr_pid) {
GST_DEBUG_OBJECT (mux, "User specified PID %d as PCR for "
"program (prog_id = %d)", pcr_pid, ts_pad->prog->pgm_number);
tsmux_program_set_pcr_pid (ts_pad->prog, pcr_pid);
goto have_pcr_pid;
}
g_free (prop_name);
gchar *pcr_sink_name = get_pmt_pcr_sink (mux, prop_name);
if (!g_strcmp0 (GST_PAD_NAME (pad), pcr_sink_name)) {
GST_DEBUG_OBJECT (mux, "User specified stream (pid=%d) as PCR for "
"program (prog_id = %d)", ts_pad->pid, ts_pad->prog->pgm_number);
tsmux_program_set_pcr_stream (ts_pad->prog, ts_pad->stream);
}
g_clear_pointer (&pcr_sink_name, g_free);
have_pcr_pid:
g_clear_pointer (&prop_name, g_free);
return ret;
@ -1383,7 +1416,7 @@ gst_base_ts_mux_aggregate_buffer (GstBaseTsMux * mux,
}
}
if (G_UNLIKELY (prog->pcr_stream == NULL)) {
if (!prog->pcr_pid && G_UNLIKELY (prog->pcr_stream == NULL)) {
/* Take the first data stream for the PCR */
GST_DEBUG_OBJECT (best,
"Use stream (pid=%d) from pad as PCR for program (prog_id = %d)",

View file

@ -440,6 +440,7 @@ tsmux_program_new (TsMux * mux, gint prog_id)
program->pmt_interval = TSMUX_DEFAULT_PMT_INTERVAL;
program->next_pmt_pcr = -1;
program->next_pcr = -1;
if (prog_id == 0) {
program->pgm_number = mux->next_pgm_no++;
@ -457,6 +458,7 @@ tsmux_program_new (TsMux * mux, gint prog_id)
program->pmt_pid = mux->next_pmt_pid++;
program->pcr_stream = NULL;
program->pcr_pid = 0;
/* SCTE35 is disabled by default */
program->scte35_pid = 0;
@ -675,6 +677,7 @@ tsmux_program_set_pcr_stream (TsMuxProgram * program, TsMuxStream * stream)
if (program->pcr_stream == stream)
return;
program->pcr_pid = 0;
if (program->pcr_stream != NULL)
tsmux_stream_pcr_unref (program->pcr_stream);
if (stream)
@ -684,6 +687,23 @@ tsmux_program_set_pcr_stream (TsMuxProgram * program, TsMuxStream * stream)
program->pmt_changed = TRUE;
}
/**
* tsmux_program_set_pcr_pid:
* @program: a #TsMuxProgram
* @pid: a PID
*
* Set @pid as the PCR PID for @program, overwriting the previously
* configured PCR PID. When pid == 0, program will have no PCR PID configured.
*/
void
tsmux_program_set_pcr_pid (TsMuxProgram * program, guint16 pid)
{
g_return_if_fail (program != NULL);
program->pcr_pid = pid;
program->pmt_changed = TRUE;
}
/**
* tsmux_get_new_pid:
* @mux: a #TsMux
@ -1552,6 +1572,25 @@ done:
return ret;
}
static gint64
write_new_prog_pcr (TsMux * mux, TsMuxProgram * prog, gint64 cur_pcr)
{
if (prog->next_pcr == -1 || cur_pcr > prog->next_pcr) {
prog->pi.flags |=
TSMUX_PACKET_FLAG_ADAPTATION | TSMUX_PACKET_FLAG_WRITE_PCR;
prog->pi.pcr = cur_pcr;
if (prog->next_pcr == -1)
prog->next_pcr = cur_pcr + mux->pcr_interval * 300;
else
prog->next_pcr += mux->pcr_interval * 300;
} else {
cur_pcr = -1;
}
return cur_pcr;
}
/**
* tsmux_write_stream_packet:
* @mux: a #TsMux
@ -1574,7 +1613,7 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
g_return_val_if_fail (mux != NULL, FALSE);
g_return_val_if_fail (stream != NULL, FALSE);
if (tsmux_stream_is_pcr (stream)) {
if (tsmux_stream_is_pcr (stream) || stream->program->pcr_pid) {
gint64 cur_ts = CLOCK_BASE;
if (tsmux_stream_get_dts (stream) != G_MININT64)
cur_ts += tsmux_stream_get_dts (stream);
@ -1590,6 +1629,29 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
new_pcr =
write_new_pcr (mux, stream, get_current_pcr (mux, cur_ts),
get_next_pcr (mux, cur_ts));
if (stream->program->pcr_pid) {
/* this should only enter block when time to send a PCR packet */
new_pcr = write_new_prog_pcr (mux, stream->program, get_current_pcr (mux,
cur_ts));
if (new_pcr != -1) {
if (!tsmux_get_buffer (mux, &buf))
return FALSE;
if (!gst_buffer_map (buf, &map, GST_MAP_WRITE))
goto fail_unmapped;
if (!tsmux_write_ts_header (mux, map.data, &stream->program->pi, 0,
NULL, NULL))
goto fail;
gst_buffer_unmap (buf, &map);
stream->program->pi.pid = stream->program->pcr_pid;
stream->program->pi.flags &= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
if (!tsmux_packet_out (mux, buf, new_pcr))
return FALSE;
}
}
}
pi->packet_start_unit_indicator = tsmux_stream_at_pes_start (stream);
@ -1635,6 +1697,9 @@ fail:
}
return FALSE;
}
fail_unmapped:
gst_clear_buffer (&buf);
return FALSE;
}
/**
@ -1667,7 +1732,10 @@ tsmux_program_free (TsMuxProgram * program)
void
tsmux_program_set_pmt_pid (TsMuxProgram * program, guint16 pmt_pid)
{
g_return_if_fail (program != NULL);
program->pmt_pid = pmt_pid;
program->pmt_changed = TRUE;
}
static gint
@ -1759,8 +1827,10 @@ tsmux_write_pmt (TsMux * mux, TsMuxProgram * program)
pmt = gst_mpegts_pmt_new ();
if (program->pcr_stream == NULL)
if ((program->pcr_stream == NULL) && (program->pcr_pid == 0))
pmt->pcr_pid = 0x1FFF;
else if (program->pcr_pid != 0)
pmt->pcr_pid = program->pcr_pid;
else
pmt->pcr_pid = tsmux_stream_get_pid (program->pcr_stream);

View file

@ -97,6 +97,8 @@ struct TsMuxProgram {
gboolean wrote_si;
TsMuxSection pmt;
TsMuxPacketInfo pi;
/* PMT version */
guint8 pmt_version;
/* trigger for writing PMT */
@ -107,6 +109,7 @@ struct TsMuxProgram {
/* Next PMT position, 27 MHz */
gint64 next_pmt_pcr;
gint64 next_pcr;
/* program ID for the PAT */
guint16 pgm_number;
@ -123,6 +126,7 @@ struct TsMuxProgram {
/* stream which carries the PCR */
TsMuxStream *pcr_stream;
guint16 pcr_pid;
/* programs TsMuxStream's */
GPtrArray *streams;
@ -229,6 +233,7 @@ gboolean tsmux_remove_stream (TsMux *mux, guint16 pid, TsMuxP
void tsmux_program_add_stream (TsMuxProgram *program, TsMuxStream *stream);
void tsmux_program_set_pcr_stream (TsMuxProgram *program, TsMuxStream *stream);
void tsmux_program_set_pcr_pid (TsMuxProgram *program, guint16 pid);
void tsmux_set_pcr_interval (TsMux * mux, guint freq);
/* writing stuff */

View file

@ -151,6 +151,9 @@ struct TsMuxStream {
guint8 id;
/* extended stream id (13818-1 Amdt 2) */
guint8 id_extended;
struct TsMuxProgram *program;
/* requested index in the PMT */
gint pmt_index;