mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
mpegtsmux: improve m2ts timestamp interpolation
* a known PCR should really belong to a packet holding that PCR * interpolation can be extended backward for initial packets (PAT, PMT)
This commit is contained in:
parent
8637fd69e5
commit
ead42a5e27
4 changed files with 60 additions and 53 deletions
|
@ -284,7 +284,7 @@ mpegtsmux_reset (MpegTsMux * mux, gboolean alloc)
|
||||||
|
|
||||||
mux->first = TRUE;
|
mux->first = TRUE;
|
||||||
mux->last_flow_ret = GST_FLOW_OK;
|
mux->last_flow_ret = GST_FLOW_OK;
|
||||||
mux->first_pcr = TRUE;
|
mux->previous_pcr = -1;
|
||||||
mux->last_ts = 0;
|
mux->last_ts = 0;
|
||||||
mux->is_delta = TRUE;
|
mux->is_delta = TRUE;
|
||||||
|
|
||||||
|
@ -1203,7 +1203,7 @@ new_packet_m2ts (MpegTsMux * mux, guint8 * data, guint len, gint64 new_pcr)
|
||||||
GST_BUFFER_TIMESTAMP (buf) = mux->last_ts;
|
GST_BUFFER_TIMESTAMP (buf) = mux->last_ts;
|
||||||
|
|
||||||
if (new_pcr < 0) {
|
if (new_pcr < 0) {
|
||||||
/* If theres no pcr in current ts packet then just add the packet
|
/* If there is no pcr in current ts packet then just add the packet
|
||||||
to the adapter for later output when we see a PCR */
|
to the adapter for later output when we see a PCR */
|
||||||
GST_LOG_OBJECT (mux, "Accumulating non-PCR packet");
|
GST_LOG_OBJECT (mux, "Accumulating non-PCR packet");
|
||||||
gst_adapter_push (mux->adapter, buf);
|
gst_adapter_push (mux->adapter, buf);
|
||||||
|
@ -1212,57 +1212,53 @@ new_packet_m2ts (MpegTsMux * mux, guint8 * data, guint len, gint64 new_pcr)
|
||||||
|
|
||||||
chunk_bytes = gst_adapter_available (mux->adapter);
|
chunk_bytes = gst_adapter_available (mux->adapter);
|
||||||
|
|
||||||
/* We have a new PCR, output anything in the adapter */
|
/* no first interpolation point yet, then this is the one,
|
||||||
if (mux->first_pcr) {
|
* otherwise it is the second interpolation point */
|
||||||
/* We can't generate sensible timestamps for anything that might
|
if (mux->previous_pcr < 0 && chunk_bytes) {
|
||||||
* be in the adapter preceding the first PCR and will hit a divide
|
mux->previous_pcr = new_pcr;
|
||||||
* by zero, so empty the adapter. This is probably a null op. */
|
mux->previous_offset = chunk_bytes;
|
||||||
gst_adapter_clear (mux->adapter);
|
GST_LOG_OBJECT (mux, "Accumulating non-PCR packet");
|
||||||
/* Warn if we threw anything away */
|
gst_adapter_push (mux->adapter, buf);
|
||||||
if (chunk_bytes) {
|
return TRUE;
|
||||||
GST_ELEMENT_WARNING (mux, STREAM, MUX,
|
|
||||||
("Discarding %d bytes from stream preceding first PCR",
|
|
||||||
chunk_bytes / M2TS_PACKET_LENGTH * NORMAL_TS_PACKET_LENGTH),
|
|
||||||
(NULL));
|
|
||||||
chunk_bytes = 0;
|
|
||||||
}
|
|
||||||
mux->first_pcr = FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chunk_bytes) {
|
/* interpolate if needed, and 2 points available */
|
||||||
/* Start the PCR offset counting at 192 bytes: At the end of the packet
|
if (chunk_bytes && (new_pcr != mux->previous_pcr)) {
|
||||||
* that had the last PCR */
|
gint64 offset = 0;
|
||||||
guint64 pcr_bytes = M2TS_PACKET_LENGTH, ts_rate;
|
|
||||||
|
|
||||||
/* Include the pending packet size to get the ts_rate right */
|
GST_LOG_OBJECT (mux, "Processing pending packets; "
|
||||||
chunk_bytes += M2TS_PACKET_LENGTH;
|
"previous pcr %" G_GINT64_FORMAT ", previous offset %d, "
|
||||||
|
"current pcr %" G_GINT64_FORMAT ", current offset %d",
|
||||||
|
mux->previous_pcr, (gint) mux->previous_offset,
|
||||||
|
new_pcr, (gint) chunk_bytes);
|
||||||
|
|
||||||
/* calculate rate based on latest and previous pcr values */
|
g_assert (chunk_bytes > mux->previous_offset);
|
||||||
ts_rate = gst_util_uint64_scale (chunk_bytes, CLOCK_FREQ_SCR,
|
while (offset < chunk_bytes) {
|
||||||
(new_pcr - mux->previous_pcr));
|
|
||||||
GST_LOG_OBJECT (mux, "Processing pending packets with ts_rate %"
|
|
||||||
G_GUINT64_FORMAT, ts_rate);
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
guint64 cur_pcr, ts;
|
guint64 cur_pcr, ts;
|
||||||
|
|
||||||
/* Loop, pulling packets of the adapter, updating their 4 byte
|
/* Loop, pulling packets of the adapter, updating their 4 byte
|
||||||
* timestamp header and pushing */
|
* timestamp header and pushing */
|
||||||
|
|
||||||
/* The header is the bottom 30 bits of the PCR, apparently not
|
/* interpolate PCR */
|
||||||
* encoded into base + ext as in the packets themselves, so
|
if (G_LIKELY (offset >= mux->previous_offset))
|
||||||
* we can just interpolate, mask and insert */
|
cur_pcr = mux->previous_pcr +
|
||||||
cur_pcr = (mux->previous_pcr +
|
gst_util_uint64_scale (offset - mux->previous_offset,
|
||||||
gst_util_uint64_scale (pcr_bytes, CLOCK_FREQ_SCR, ts_rate));
|
new_pcr - mux->previous_pcr, chunk_bytes - mux->previous_offset);
|
||||||
|
else
|
||||||
|
cur_pcr = mux->previous_pcr -
|
||||||
|
gst_util_uint64_scale (mux->previous_offset - offset,
|
||||||
|
new_pcr - mux->previous_pcr, chunk_bytes - mux->previous_offset);
|
||||||
|
|
||||||
ts = gst_adapter_prev_timestamp (mux->adapter, NULL);
|
ts = gst_adapter_prev_timestamp (mux->adapter, NULL);
|
||||||
out_buf = gst_adapter_take_buffer (mux->adapter, M2TS_PACKET_LENGTH);
|
out_buf = gst_adapter_take_buffer (mux->adapter, M2TS_PACKET_LENGTH);
|
||||||
if (G_UNLIKELY (!out_buf))
|
g_assert (out_buf);
|
||||||
break;
|
offset += M2TS_PACKET_LENGTH;
|
||||||
|
|
||||||
gst_buffer_set_caps (out_buf, GST_PAD_CAPS (mux->srcpad));
|
gst_buffer_set_caps (out_buf, GST_PAD_CAPS (mux->srcpad));
|
||||||
GST_BUFFER_TIMESTAMP (out_buf) = ts;
|
GST_BUFFER_TIMESTAMP (out_buf) = ts;
|
||||||
|
|
||||||
/* Write the 4 byte timestamp value, bottom 30 bits only = PCR */
|
/* The header is the bottom 30 bits of the PCR, apparently not
|
||||||
|
* encoded into base + ext as in the packets themselves */
|
||||||
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (out_buf), cur_pcr & 0x3FFFFFFF);
|
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (out_buf), cur_pcr & 0x3FFFFFFF);
|
||||||
|
|
||||||
GST_LOG_OBJECT (mux, "Outputting a packet of length %d PCR %"
|
GST_LOG_OBJECT (mux, "Outputting a packet of length %d PCR %"
|
||||||
|
@ -1272,7 +1268,6 @@ new_packet_m2ts (MpegTsMux * mux, guint8 * data, guint len, gint64 new_pcr)
|
||||||
mux->last_flow_ret = ret;
|
mux->last_flow_ret = ret;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
pcr_bytes += M2TS_PACKET_LENGTH;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1288,7 +1283,10 @@ new_packet_m2ts (MpegTsMux * mux, guint8 * data, guint len, gint64 new_pcr)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
mux->previous_pcr = new_pcr;
|
if (new_pcr != mux->previous_pcr) {
|
||||||
|
mux->previous_pcr = new_pcr;
|
||||||
|
mux->previous_offset = -M2TS_PACKET_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,8 +153,8 @@ struct MpegTsMux {
|
||||||
GstClockTime last_ts;
|
GstClockTime last_ts;
|
||||||
|
|
||||||
/* m2ts specific */
|
/* m2ts specific */
|
||||||
gboolean first_pcr;
|
|
||||||
gint64 previous_pcr;
|
gint64 previous_pcr;
|
||||||
|
gint64 previous_offset;
|
||||||
GstAdapter *adapter;
|
GstAdapter *adapter;
|
||||||
|
|
||||||
/* output buffer aggregation */
|
/* output buffer aggregation */
|
||||||
|
|
|
@ -108,6 +108,10 @@
|
||||||
/* Times per second to write PCR */
|
/* Times per second to write PCR */
|
||||||
#define TSMUX_DEFAULT_PCR_FREQ (25)
|
#define TSMUX_DEFAULT_PCR_FREQ (25)
|
||||||
|
|
||||||
|
/* Base for all written PCR and DTS/PTS,
|
||||||
|
* so we have some slack to go backwards */
|
||||||
|
#define CLOCK_BASE (TSMUX_CLOCK_FREQ * 10 * 360)
|
||||||
|
|
||||||
static gboolean tsmux_write_pat (TsMux * mux);
|
static gboolean tsmux_write_pat (TsMux * mux);
|
||||||
static gboolean tsmux_write_pmt (TsMux * mux, TsMuxProgram * program);
|
static gboolean tsmux_write_pmt (TsMux * mux, TsMuxProgram * program);
|
||||||
|
|
||||||
|
@ -431,13 +435,13 @@ tsmux_find_stream (TsMux * mux, guint16 pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
tsmux_packet_out (TsMux * mux)
|
tsmux_packet_out (TsMux * mux, gint64 pcr)
|
||||||
{
|
{
|
||||||
if (G_UNLIKELY (mux->write_func == NULL))
|
if (G_UNLIKELY (mux->write_func == NULL))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
return mux->write_func (mux->packet_buf, TSMUX_PACKET_LENGTH,
|
return mux->write_func (mux->packet_buf, TSMUX_PACKET_LENGTH,
|
||||||
mux->write_func_data, mux->new_pcr);
|
mux->write_func_data, pcr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -702,18 +706,17 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
|
||||||
guint payload_len, payload_offs;
|
guint payload_len, payload_offs;
|
||||||
TsMuxPacketInfo *pi = &stream->pi;
|
TsMuxPacketInfo *pi = &stream->pi;
|
||||||
gboolean res;
|
gboolean res;
|
||||||
|
gint64 cur_pcr = -1;
|
||||||
|
|
||||||
|
|
||||||
mux->new_pcr = -1;
|
|
||||||
g_return_val_if_fail (mux != NULL, FALSE);
|
g_return_val_if_fail (mux != NULL, FALSE);
|
||||||
g_return_val_if_fail (stream != NULL, FALSE);
|
g_return_val_if_fail (stream != NULL, FALSE);
|
||||||
|
|
||||||
if (tsmux_stream_is_pcr (stream)) {
|
if (tsmux_stream_is_pcr (stream)) {
|
||||||
gint64 cur_pcr = 0;
|
|
||||||
gint64 cur_pts = tsmux_stream_get_pts (stream);
|
gint64 cur_pts = tsmux_stream_get_pts (stream);
|
||||||
gboolean write_pat;
|
gboolean write_pat;
|
||||||
GList *cur;
|
GList *cur;
|
||||||
|
|
||||||
|
cur_pcr = 0;
|
||||||
if (cur_pts != -1) {
|
if (cur_pts != -1) {
|
||||||
TS_DEBUG ("TS for PCR stream is %" G_GINT64_FORMAT, cur_pts);
|
TS_DEBUG ("TS for PCR stream is %" G_GINT64_FORMAT, cur_pts);
|
||||||
}
|
}
|
||||||
|
@ -724,6 +727,8 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
|
||||||
cur_pcr = (cur_pts - TSMUX_PCR_OFFSET) *
|
cur_pcr = (cur_pts - TSMUX_PCR_OFFSET) *
|
||||||
(TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ);
|
(TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ);
|
||||||
|
|
||||||
|
cur_pcr += CLOCK_BASE * (TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ);
|
||||||
|
|
||||||
/* Need to decide whether to write a new PCR in this packet */
|
/* Need to decide whether to write a new PCR in this packet */
|
||||||
if (stream->last_pcr == -1 ||
|
if (stream->last_pcr == -1 ||
|
||||||
(cur_pcr - stream->last_pcr >
|
(cur_pcr - stream->last_pcr >
|
||||||
|
@ -733,7 +738,8 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
|
||||||
TSMUX_PACKET_FLAG_ADAPTATION | TSMUX_PACKET_FLAG_WRITE_PCR;
|
TSMUX_PACKET_FLAG_ADAPTATION | TSMUX_PACKET_FLAG_WRITE_PCR;
|
||||||
stream->pi.pcr = cur_pcr;
|
stream->pi.pcr = cur_pcr;
|
||||||
stream->last_pcr = cur_pcr;
|
stream->last_pcr = cur_pcr;
|
||||||
mux->new_pcr = cur_pcr;
|
} else {
|
||||||
|
cur_pcr = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if we need to rewrite pat */
|
/* check if we need to rewrite pat */
|
||||||
|
@ -771,8 +777,13 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi->packet_start_unit_indicator = tsmux_stream_at_pes_start (stream);
|
pi->packet_start_unit_indicator = tsmux_stream_at_pes_start (stream);
|
||||||
if (pi->packet_start_unit_indicator)
|
if (pi->packet_start_unit_indicator) {
|
||||||
tsmux_stream_initialize_pes_packet (stream);
|
tsmux_stream_initialize_pes_packet (stream);
|
||||||
|
if (stream->dts != -1)
|
||||||
|
stream->dts += CLOCK_BASE;
|
||||||
|
if (stream->pts != -1)
|
||||||
|
stream->pts += CLOCK_BASE;
|
||||||
|
}
|
||||||
pi->stream_avail = tsmux_stream_bytes_avail (stream);
|
pi->stream_avail = tsmux_stream_bytes_avail (stream);
|
||||||
|
|
||||||
if (!tsmux_write_ts_header (mux->packet_buf, pi, &payload_len, &payload_offs))
|
if (!tsmux_write_ts_header (mux->packet_buf, pi, &payload_len, &payload_offs))
|
||||||
|
@ -782,7 +793,7 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
|
||||||
payload_len))
|
payload_len))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
res = tsmux_packet_out (mux);
|
res = tsmux_packet_out (mux, cur_pcr);
|
||||||
|
|
||||||
/* Reset all dynamic flags */
|
/* Reset all dynamic flags */
|
||||||
stream->pi.flags &= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
|
stream->pi.flags &= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
|
||||||
|
@ -853,11 +864,10 @@ tsmux_write_section (TsMux * mux, TsMuxSection * section)
|
||||||
cur_in += payload_len;
|
cur_in += payload_len;
|
||||||
payload_remain -= payload_len;
|
payload_remain -= payload_len;
|
||||||
|
|
||||||
if (G_UNLIKELY (!tsmux_packet_out (mux))) {
|
/* we do not write PCR in section */
|
||||||
mux->new_pcr = -1;
|
if (G_UNLIKELY (!tsmux_packet_out (mux, -1))) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
mux->new_pcr = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
|
@ -167,7 +167,6 @@ 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];
|
||||||
gint64 new_pcr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* create/free new muxer session */
|
/* create/free new muxer session */
|
||||||
|
|
Loading…
Reference in a new issue