tsmux: refactor logic for when to (re)transmit tables

In order to output them at regular intervals in the bitstream
when a bitrate is specified.
This commit is contained in:
Mathieu Duponchelle 2019-05-15 01:35:04 +02:00 committed by Mathieu Duponchelle
parent 52efb62876
commit 9190541e3c
2 changed files with 157 additions and 122 deletions

View file

@ -107,7 +107,7 @@
#define TSMUX_PCR_OFFSET (TSMUX_CLOCK_FREQ / 8)
/* Times per second to write PCR */
#define TSMUX_DEFAULT_PCR_FREQ (25)
#define TSMUX_DEFAULT_PCR_FREQ (50)
/* Base for all written PCR and DTS/PTS,
* so we have some slack to go backwards */
@ -143,13 +143,14 @@ tsmux_new (void)
mux->next_stream_pid = TSMUX_START_ES_PID;
mux->pat_changed = TRUE;
mux->last_pat_ts = G_MININT64;
mux->next_pat_pcr = -1;
mux->pat_interval = TSMUX_DEFAULT_PAT_INTERVAL;
mux->si_changed = TRUE;
mux->last_si_ts = G_MININT64;
mux->si_interval = TSMUX_DEFAULT_SI_INTERVAL;
mux->next_si_pcr = -1;
mux->si_sections = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify) tsmux_section_free);
@ -262,7 +263,7 @@ tsmux_resend_pat (TsMux * mux)
{
g_return_if_fail (mux != NULL);
mux->last_pat_ts = G_MININT64;
mux->next_pat_pcr = -1;
}
/**
@ -309,7 +310,7 @@ tsmux_resend_si (TsMux * mux)
{
g_return_if_fail (mux != NULL);
mux->last_si_ts = G_MININT64;
mux->next_si_pcr = -1;
}
/**
@ -415,9 +416,10 @@ tsmux_program_new (TsMux * mux, gint prog_id)
program = g_slice_new0 (TsMuxProgram);
program->pmt_changed = TRUE;
program->last_pmt_ts = G_MININT64;
program->pmt_interval = TSMUX_DEFAULT_PMT_INTERVAL;
program->next_pmt_pcr = -1;
if (prog_id == 0) {
program->pgm_number = mux->next_pgm_no++;
while (g_list_find_custom (mux->programs, &program->pgm_number,
@ -490,7 +492,7 @@ tsmux_resend_pmt (TsMuxProgram * program)
{
g_return_if_fail (program != NULL);
program->last_pmt_ts = G_MININT64;
program->next_pmt_pcr = -1;
}
/**
@ -1057,14 +1059,129 @@ tsmux_write_null_ts_header (guint8 * buf)
*buf++ = 0x10;
}
static gint64
get_current_pcr (TsMux * mux, gint64 cur_ts)
{
if (mux->bitrate)
return (CLOCK_BASE - TSMUX_PCR_OFFSET) * 300 +
gst_util_uint64_scale (mux->n_bytes * 8, TSMUX_SYS_CLOCK_FREQ,
mux->bitrate);
else if (cur_ts != G_MININT64)
return (cur_ts -
TSMUX_PCR_OFFSET) * (TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ);
else
return 0;
}
static gint64
write_new_pcr (TsMux * mux, TsMuxStream * stream, gint64 cur_pcr)
{
if (stream->next_pcr == -1 || cur_pcr > stream->next_pcr) {
stream->pi.flags |=
TSMUX_PACKET_FLAG_ADAPTATION | TSMUX_PACKET_FLAG_WRITE_PCR;
stream->pi.pcr = cur_pcr;
if (stream->next_pcr == -1)
stream->next_pcr =
cur_pcr + TSMUX_SYS_CLOCK_FREQ / TSMUX_DEFAULT_PCR_FREQ;
else
stream->next_pcr += TSMUX_SYS_CLOCK_FREQ / TSMUX_DEFAULT_PCR_FREQ;
} else {
cur_pcr = -1;
}
return cur_pcr;
}
static gboolean
pad_stream (TsMux * mux, TsMuxStream * stream, gint64 cur_ts, gint64 * cur_pcr)
rewrite_si (TsMux * mux, gint64 cur_ts)
{
gboolean write_pat;
gboolean write_si;
GList *cur;
gint64 cur_pcr;
cur_pcr = get_current_pcr (mux, cur_ts);
/* check if we need to rewrite pat */
if (mux->next_pat_pcr == -1 || mux->pat_changed)
write_pat = TRUE;
else if (cur_pcr > mux->next_pat_pcr)
write_pat = TRUE;
else
write_pat = FALSE;
if (write_pat) {
if (mux->next_pat_pcr == -1)
mux->next_pat_pcr = cur_pcr + mux->pat_interval * 300;
else
mux->next_pat_pcr += mux->pat_interval * 300;
if (!tsmux_write_pat (mux))
return FALSE;
cur_pcr = get_current_pcr (mux, cur_ts);
}
/* check if we need to rewrite sit */
if (mux->next_si_pcr == -1 || mux->si_changed)
write_si = TRUE;
else if (cur_pcr > mux->next_si_pcr)
write_si = TRUE;
else
write_si = FALSE;
if (write_si) {
if (mux->next_si_pcr == -1)
mux->next_si_pcr = cur_pcr + mux->si_interval * 300;
else
mux->next_si_pcr += mux->si_interval * 300;
if (!tsmux_write_si (mux))
return FALSE;
cur_pcr = get_current_pcr (mux, cur_ts);
}
/* check if we need to rewrite any of the current pmts */
for (cur = mux->programs; cur; cur = cur->next) {
TsMuxProgram *program = (TsMuxProgram *) cur->data;
gboolean write_pmt;
if (program->next_pmt_pcr == -1 || program->pmt_changed)
write_pmt = TRUE;
else if (cur_pcr > program->next_pmt_pcr)
write_pmt = TRUE;
else
write_pmt = FALSE;
if (write_pmt) {
if (program->next_pmt_pcr == -1)
program->next_pmt_pcr = cur_pcr + program->pmt_interval * 300;
else
program->next_pmt_pcr += program->pmt_interval * 300;
if (!tsmux_write_pmt (mux, program))
return FALSE;
cur_pcr = get_current_pcr (mux, cur_ts);
}
}
return TRUE;
}
static gboolean
pad_stream (TsMux * mux, TsMuxStream * stream, gint64 cur_ts)
{
guint64 bitrate;
GstBuffer *buf = NULL;
GstMapInfo map;
gboolean ret = TRUE;
if (!mux->bitrate)
goto done;
do {
if (GST_CLOCK_STIME_IS_VALID (cur_ts)) {
GstClockTimeDiff diff;
@ -1074,11 +1191,6 @@ pad_stream (TsMux * mux, TsMuxStream * stream, gint64 cur_ts, gint64 * cur_pcr)
diff = GST_CLOCK_DIFF (stream->first_ts, cur_ts);
*cur_pcr =
(CLOCK_BASE - TSMUX_PCR_OFFSET) * 300 +
gst_util_uint64_scale (mux->n_bytes * 8, TSMUX_SYS_CLOCK_FREQ,
mux->bitrate);
if (diff) {
bitrate =
gst_util_uint64_scale (mux->n_bytes * 8, TSMUX_CLOCK_FREQ, diff);
@ -1086,8 +1198,16 @@ pad_stream (TsMux * mux, TsMuxStream * stream, gint64 cur_ts, gint64 * cur_pcr)
GST_LOG ("Transport stream bitrate: %" G_GUINT64_FORMAT, bitrate);
if (bitrate < mux->bitrate) {
gint64 new_pcr;
guint payload_len, payload_offs;
GST_LOG ("Padding transport stream");
if (!rewrite_si (mux, cur_ts)) {
ret = FALSE;
goto done;
}
if (!tsmux_get_buffer (mux, &buf)) {
ret = FALSE;
goto done;
@ -1095,31 +1215,19 @@ pad_stream (TsMux * mux, TsMuxStream * stream, gint64 cur_ts, gint64 * cur_pcr)
gst_buffer_map (buf, &map, GST_MAP_READ);
if (stream->next_pcr == -1 || *cur_pcr > stream->next_pcr) {
guint payload_len, payload_offs;
stream->pi.flags |=
TSMUX_PACKET_FLAG_ADAPTATION | TSMUX_PACKET_FLAG_WRITE_PCR;
stream->pi.pcr = *cur_pcr;
if (stream->next_pcr == -1)
stream->next_pcr =
*cur_pcr + TSMUX_SYS_CLOCK_FREQ / TSMUX_DEFAULT_PCR_FREQ;
else
stream->next_pcr += TSMUX_SYS_CLOCK_FREQ / TSMUX_DEFAULT_PCR_FREQ;
if ((new_pcr =
write_new_pcr (mux, stream, get_current_pcr (mux,
cur_ts)) != -1))
tsmux_write_ts_header (map.data, &stream->pi, &payload_len,
&payload_offs, 0);
stream->pi.flags &= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
} else {
else
tsmux_write_null_ts_header (map.data);
*cur_pcr = -1;
}
gst_buffer_unmap (buf, &map);
if (!(ret = tsmux_packet_out (mux, buf, *cur_pcr)))
stream->pi.flags &= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
if (!(ret = tsmux_packet_out (mux, buf, new_pcr)))
goto done;
}
} else {
@ -1149,7 +1257,7 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
guint payload_len, payload_offs;
TsMuxPacketInfo *pi = &stream->pi;
gboolean res;
gint64 cur_pcr = -1;
gint64 new_pcr = -1;
GstBuffer *buf = NULL;
GstMapInfo map;
@ -1157,94 +1265,20 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
g_return_val_if_fail (stream != NULL, FALSE);
if (tsmux_stream_is_pcr (stream)) {
gint64 cur_ts;
gboolean write_pat;
gboolean write_si;
GList *cur;
gint64 cur_ts = CLOCK_BASE;
if (tsmux_stream_get_dts (stream) != G_MININT64)
cur_ts = tsmux_stream_get_dts (stream);
cur_ts += tsmux_stream_get_dts (stream);
else
cur_ts = tsmux_stream_get_pts (stream);
cur_ts += tsmux_stream_get_pts (stream);
cur_pcr = 0;
if (!rewrite_si (mux, cur_ts))
goto fail;
if (mux->bitrate) {
if (!pad_stream (mux, stream, cur_ts, &cur_pcr))
goto fail;
} else {
/* FIXME: The current PCR needs more careful calculation than just
* writing a fixed offset */
if (cur_ts != G_MININT64) {
TS_DEBUG ("TS for PCR stream is %" G_GINT64_FORMAT, cur_ts);
/* CLOCK_BASE >= TSMUX_PCR_OFFSET */
cur_ts += CLOCK_BASE;
cur_pcr = (cur_ts - TSMUX_PCR_OFFSET) *
(TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ);
}
}
if (!pad_stream (mux, stream, cur_ts))
goto fail;
/* Need to decide whether to write a new PCR in this packet */
if (stream->next_pcr == -1 || cur_pcr > stream->next_pcr) {
stream->pi.flags |=
TSMUX_PACKET_FLAG_ADAPTATION | TSMUX_PACKET_FLAG_WRITE_PCR;
stream->pi.pcr = cur_pcr;
if (stream->next_pcr == -1)
stream->next_pcr =
cur_pcr + TSMUX_SYS_CLOCK_FREQ / TSMUX_DEFAULT_PCR_FREQ;
else
stream->next_pcr += TSMUX_SYS_CLOCK_FREQ / TSMUX_DEFAULT_PCR_FREQ;
} else {
cur_pcr = -1;
}
/* check if we need to rewrite pat */
if (mux->last_pat_ts == G_MININT64 || mux->pat_changed)
write_pat = TRUE;
else if (cur_ts >= mux->last_pat_ts + mux->pat_interval)
write_pat = TRUE;
else
write_pat = FALSE;
if (write_pat) {
mux->last_pat_ts = cur_ts;
if (!tsmux_write_pat (mux))
return FALSE;
}
/* check if we need to rewrite sit */
if (mux->last_si_ts == G_MININT64 || mux->si_changed)
write_si = TRUE;
else if (cur_ts >= mux->last_si_ts + mux->si_interval)
write_si = TRUE;
else
write_si = FALSE;
if (write_si) {
mux->last_si_ts = cur_ts;
if (!tsmux_write_si (mux))
return FALSE;
}
/* check if we need to rewrite any of the current pmts */
for (cur = mux->programs; cur; cur = cur->next) {
TsMuxProgram *program = (TsMuxProgram *) cur->data;
gboolean write_pmt;
if (program->last_pmt_ts == G_MININT64 || program->pmt_changed)
write_pmt = TRUE;
else if (cur_ts >= program->last_pmt_ts + program->pmt_interval)
write_pmt = TRUE;
else
write_pmt = FALSE;
if (write_pmt) {
program->last_pmt_ts = cur_ts;
if (!tsmux_write_pmt (mux, program))
return FALSE;
}
}
new_pcr = write_new_pcr (mux, stream, get_current_pcr (mux, cur_ts));
}
pi->packet_start_unit_indicator = tsmux_stream_at_pes_start (stream);
@ -1274,7 +1308,7 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
gst_buffer_unmap (buf, &map);
GST_DEBUG ("Writing PES of size %d", (int) gst_buffer_get_size (buf));
res = tsmux_packet_out (mux, buf, cur_pcr);
res = tsmux_packet_out (mux, buf, new_pcr);
/* Reset all dynamic flags */
stream->pi.flags &= TSMUX_PACKET_FLAG_PES_FULL_HEADER;

View file

@ -119,8 +119,9 @@ struct TsMuxProgram {
/* interval between PMT in MPEG PTS clock time */
guint pmt_interval;
/* last time PMT written in MPEG PTS clock time */
gint64 last_pmt_ts;
/* Next PMT position, 27 MHz */
gint64 next_pmt_pcr;
/* program ID for the PAT */
guint16 pgm_number;
@ -160,15 +161,15 @@ struct TsMux {
gboolean pat_changed;
/* interval between PAT in MPEG PTS clock time */
guint pat_interval;
/* last time PAT written in MPEG PTS clock time */
gint64 last_pat_ts;
/* Next PAT position, 27 MHz */
gint64 next_pat_pcr;
/* trigger writing Service Information Tables */
gboolean si_changed;
/* interval between SIT in MPEG PTS clock time */
guint si_interval;
/* last time SIT written in MPEG PTS clock time */
gint64 last_si_ts;
/* Next SIT position, 27 MHz */
gint64 next_si_pcr;
/* callback to write finished packet */
TsMuxWriteFunc write_func;