mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
flv: Tag timestamp are DTS not PTS
The tags in FLV are DTS. In audio cases, and for many video format this makes no difference, but for AVC with B-Frames, PTS need to be computed from composition timestamp CTS, with PTS = DTS + CTS. https://bugzilla.gnome.org/show_bug.cgi?id=731352
This commit is contained in:
parent
a98341397d
commit
ff2bce7b26
3 changed files with 74 additions and 61 deletions
|
@ -93,7 +93,7 @@ G_DEFINE_TYPE (GstFlvDemux, gst_flv_demux, GST_TYPE_ELEMENT);
|
|||
/* 1 byte of tag type + 3 bytes of tag data size */
|
||||
#define FLV_TAG_TYPE_SIZE 4
|
||||
|
||||
/* two seconds - consider pts are resynced to another base if this different */
|
||||
/* two seconds - consider dts are resynced to another base if this different */
|
||||
#define RESYNC_THRESHOLD 2000
|
||||
|
||||
static gboolean flv_demux_handle_seek_push (GstFlvDemux * demux,
|
||||
|
@ -889,23 +889,23 @@ gst_flv_demux_push_tags (GstFlvDemux * demux)
|
|||
}
|
||||
|
||||
static gboolean
|
||||
gst_flv_demux_update_resync (GstFlvDemux * demux, guint32 pts, gboolean discont,
|
||||
gst_flv_demux_update_resync (GstFlvDemux * demux, guint32 dts, gboolean discont,
|
||||
guint32 * last, GstClockTime * offset)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gint32 dpts = pts - *last;
|
||||
if (!discont && ABS (dpts) >= RESYNC_THRESHOLD) {
|
||||
gint32 ddts = dts - *last;
|
||||
if (!discont && ABS (ddts) >= RESYNC_THRESHOLD) {
|
||||
/* Theoretically, we should use substract the duration of the last buffer,
|
||||
but this demuxer sends no durations on buffers, not sure if it cannot
|
||||
know, or just does not care to calculate. */
|
||||
*offset -= dpts * GST_MSECOND;
|
||||
*offset -= ddts * GST_MSECOND;
|
||||
GST_WARNING_OBJECT (demux,
|
||||
"Large pts gap (%" G_GINT32_FORMAT " ms), assuming resync, offset now %"
|
||||
GST_TIME_FORMAT "", dpts, GST_TIME_ARGS (*offset));
|
||||
"Large dts gap (%" G_GINT32_FORMAT " ms), assuming resync, offset now %"
|
||||
GST_TIME_FORMAT "", ddts, GST_TIME_ARGS (*offset));
|
||||
|
||||
ret = TRUE;
|
||||
}
|
||||
*last = pts;
|
||||
*last = dts;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1113,7 +1113,8 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
|
|||
}
|
||||
|
||||
/* Fill buffer with data */
|
||||
GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND + demux->audio_time_offset;
|
||||
GST_BUFFER_PTS (outbuf) = pts * GST_MSECOND + demux->audio_time_offset;
|
||||
GST_BUFFER_DTS (outbuf) = GST_BUFFER_PTS (outbuf);
|
||||
GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
|
||||
GST_BUFFER_OFFSET (outbuf) = demux->audio_offset++;
|
||||
GST_BUFFER_OFFSET_END (outbuf) = demux->audio_offset;
|
||||
|
@ -1310,7 +1311,8 @@ static GstFlowReturn
|
|||
gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
guint32 pts = 0, codec_data = 1, pts_ext = 0;
|
||||
guint32 dts = 0, codec_data = 1, dts_ext = 0;
|
||||
gint32 cts = 0;
|
||||
gboolean keyframe = FALSE;
|
||||
guint8 flags = 0, codec_tag = 0;
|
||||
GstBuffer *outbuf;
|
||||
|
@ -1337,14 +1339,14 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
|
|||
data = map.data;
|
||||
|
||||
/* Grab information about video tag */
|
||||
pts = GST_READ_UINT24_BE (data);
|
||||
/* read the pts extension to 32 bits integer */
|
||||
pts_ext = GST_READ_UINT8 (data + 3);
|
||||
dts = GST_READ_UINT24_BE (data);
|
||||
/* read the dts extension to 32 bits integer */
|
||||
dts_ext = GST_READ_UINT8 (data + 3);
|
||||
/* Combine them */
|
||||
pts |= pts_ext << 24;
|
||||
dts |= dts_ext << 24;
|
||||
|
||||
GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X (%d)", data[0], data[1],
|
||||
data[2], data[3], pts);
|
||||
GST_LOG_OBJECT (demux, "dts bytes %02X %02X %02X %02X (%d)", data[0], data[1],
|
||||
data[2], data[3], dts);
|
||||
|
||||
/* Skip the stream id and go directly to the flags */
|
||||
flags = GST_READ_UINT8 (data + 7);
|
||||
|
@ -1358,18 +1360,12 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
|
|||
if (codec_tag == 4 || codec_tag == 5) {
|
||||
codec_data = 2;
|
||||
} else if (codec_tag == 7) {
|
||||
gint32 cts;
|
||||
|
||||
codec_data = 5;
|
||||
|
||||
cts = GST_READ_UINT24_BE (data + 9);
|
||||
cts = (cts + 0xff800000) ^ 0xff800000;
|
||||
|
||||
GST_LOG_OBJECT (demux, "got cts %d", cts);
|
||||
|
||||
/* avoid negative overflow */
|
||||
if (cts >= 0 || pts >= -cts)
|
||||
pts += cts;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (demux, "video tag with codec tag %u, keyframe (%d) "
|
||||
|
@ -1493,14 +1489,18 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
|
|||
}
|
||||
}
|
||||
|
||||
/* detect (and deem to be resyncs) large pts gaps */
|
||||
if (gst_flv_demux_update_resync (demux, pts, demux->video_need_discont,
|
||||
&demux->last_video_pts, &demux->video_time_offset)) {
|
||||
/* detect (and deem to be resyncs) large dts gaps */
|
||||
if (gst_flv_demux_update_resync (demux, dts, demux->video_need_discont,
|
||||
&demux->last_video_dts, &demux->video_time_offset)) {
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC);
|
||||
}
|
||||
|
||||
/* Fill buffer with data */
|
||||
GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND + demux->video_time_offset;
|
||||
GST_LOG_OBJECT (demux, "dts %u pts %u cts %d", dts, dts + cts, cts);
|
||||
|
||||
GST_BUFFER_PTS (outbuf) =
|
||||
(dts + cts) * GST_MSECOND + demux->video_time_offset;
|
||||
GST_BUFFER_DTS (outbuf) = dts * GST_MSECOND + demux->video_time_offset;
|
||||
GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
|
||||
GST_BUFFER_OFFSET (outbuf) = demux->video_offset++;
|
||||
GST_BUFFER_OFFSET_END (outbuf) = demux->video_offset;
|
||||
|
@ -1543,10 +1543,10 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
|
|||
}
|
||||
|
||||
GST_LOG_OBJECT (demux,
|
||||
"pushing %" G_GSIZE_FORMAT " bytes buffer at pts %" GST_TIME_FORMAT
|
||||
"pushing %" G_GSIZE_FORMAT " bytes buffer at dts %" GST_TIME_FORMAT
|
||||
" with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
|
||||
", keyframe (%d)", gst_buffer_get_size (outbuf),
|
||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
|
||||
GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)),
|
||||
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
|
||||
keyframe);
|
||||
|
||||
|
@ -1594,7 +1594,7 @@ static GstClockTime
|
|||
gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
|
||||
GstBuffer * buffer, size_t * tag_size)
|
||||
{
|
||||
guint32 pts = 0, pts_ext = 0;
|
||||
guint32 dts = 0, dts_ext = 0;
|
||||
guint32 tag_data_size;
|
||||
guint8 type;
|
||||
gboolean keyframe = TRUE;
|
||||
|
@ -1636,15 +1636,15 @@ gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
|
|||
|
||||
data += 4;
|
||||
|
||||
GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X", data[0], data[1],
|
||||
GST_LOG_OBJECT (demux, "dts bytes %02X %02X %02X %02X", data[0], data[1],
|
||||
data[2], data[3]);
|
||||
|
||||
/* Grab timestamp of tag tag */
|
||||
pts = GST_READ_UINT24_BE (data);
|
||||
/* read the pts extension to 32 bits integer */
|
||||
pts_ext = GST_READ_UINT8 (data + 3);
|
||||
dts = GST_READ_UINT24_BE (data);
|
||||
/* read the dts extension to 32 bits integer */
|
||||
dts_ext = GST_READ_UINT8 (data + 3);
|
||||
/* Combine them */
|
||||
pts |= pts_ext << 24;
|
||||
dts |= dts_ext << 24;
|
||||
|
||||
if (type == 9) {
|
||||
data += 7;
|
||||
|
@ -1652,8 +1652,8 @@ gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
|
|||
keyframe = ((data[0] >> 4) == 1);
|
||||
}
|
||||
|
||||
ret = pts * GST_MSECOND;
|
||||
GST_LOG_OBJECT (demux, "pts: %" GST_TIME_FORMAT, GST_TIME_ARGS (ret));
|
||||
ret = dts * GST_MSECOND;
|
||||
GST_LOG_OBJECT (demux, "dts: %" GST_TIME_FORMAT, GST_TIME_ARGS (ret));
|
||||
|
||||
if (index && !demux->indexed && (type == 9 || (type == 8
|
||||
&& !demux->has_video))) {
|
||||
|
@ -1823,7 +1823,7 @@ gst_flv_demux_cleanup (GstFlvDemux * demux)
|
|||
demux->index_max_time = 0;
|
||||
|
||||
demux->audio_start = demux->video_start = GST_CLOCK_TIME_NONE;
|
||||
demux->last_audio_pts = demux->last_video_pts = 0;
|
||||
demux->last_audio_pts = demux->last_video_dts = 0;
|
||||
demux->audio_time_offset = demux->video_time_offset = 0;
|
||||
|
||||
demux->no_more_pads = FALSE;
|
||||
|
|
|
@ -118,7 +118,7 @@ struct _GstFlvDemux
|
|||
gboolean got_par;
|
||||
GstBuffer * video_codec_data;
|
||||
GstClockTime video_start;
|
||||
guint32 last_video_pts;
|
||||
guint32 last_video_dts;
|
||||
GstClockTime video_time_offset;
|
||||
gdouble framerate;
|
||||
|
||||
|
|
|
@ -1001,12 +1001,27 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
|
|||
GstBuffer *tag;
|
||||
GstMapInfo map;
|
||||
guint size;
|
||||
guint32 timestamp =
|
||||
(GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) ? GST_BUFFER_TIMESTAMP (buffer) /
|
||||
GST_MSECOND : cpad->last_timestamp / GST_MSECOND;
|
||||
guint32 pts, dts, cts;
|
||||
guint8 *data, *bdata;
|
||||
gsize bsize;
|
||||
|
||||
if (GST_BUFFER_DTS_IS_VALID (buffer))
|
||||
dts = GST_BUFFER_DTS (buffer) / GST_MSECOND;
|
||||
else
|
||||
dts = cpad->last_timestamp / GST_MSECOND;
|
||||
|
||||
if (GST_BUFFER_PTS_IS_VALID (buffer))
|
||||
pts = GST_BUFFER_PTS (buffer) / GST_MSECOND;
|
||||
else
|
||||
pts = dts;
|
||||
|
||||
if (pts > dts)
|
||||
cts = pts - dts;
|
||||
else
|
||||
cts = 0;
|
||||
|
||||
GST_LOG_OBJECT (mux, "got pts %i dts %i cts %i\n", pts, dts, cts);
|
||||
|
||||
gst_buffer_map (buffer, &map, GST_MAP_READ);
|
||||
bdata = map.data;
|
||||
bsize = map.size;
|
||||
|
@ -1028,7 +1043,6 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
|
|||
size += 4;
|
||||
|
||||
_gst_buffer_new_and_alloc (size, &tag, &data);
|
||||
GST_BUFFER_TIMESTAMP (tag) = timestamp * GST_MSECOND;
|
||||
memset (data, 0, size);
|
||||
|
||||
data[0] = (cpad->video) ? 9 : 8;
|
||||
|
@ -1037,12 +1051,8 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
|
|||
data[2] = ((size - 11 - 4) >> 8) & 0xff;
|
||||
data[3] = ((size - 11 - 4) >> 0) & 0xff;
|
||||
|
||||
/* wrap the timestamp every G_MAXINT32 miliseconds */
|
||||
timestamp &= 0x7fffffff;
|
||||
data[4] = (timestamp >> 16) & 0xff;
|
||||
data[5] = (timestamp >> 8) & 0xff;
|
||||
data[6] = (timestamp >> 0) & 0xff;
|
||||
data[7] = (timestamp >> 24) & 0xff;
|
||||
GST_WRITE_UINT24_BE (data + 4, dts);
|
||||
data[7] = (((guint) dts) >> 24) & 0xff;
|
||||
|
||||
data[8] = data[9] = data[10] = 0;
|
||||
|
||||
|
@ -1055,11 +1065,14 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
|
|||
data[11] |= cpad->video_codec & 0x0f;
|
||||
|
||||
if (cpad->video_codec == 7) {
|
||||
data[12] = is_codec_data ? 0 : 1;
|
||||
|
||||
/* FIXME: what to do about composition time */
|
||||
data[13] = data[14] = data[15] = 0;
|
||||
|
||||
if (is_codec_data) {
|
||||
data[12] = 0;
|
||||
GST_WRITE_UINT24_BE (data + 13, 0);
|
||||
} else {
|
||||
/* ACV NALU */
|
||||
data[12] = 1;
|
||||
GST_WRITE_UINT24_BE (data + 13, cts);
|
||||
}
|
||||
memcpy (data + 11 + 1 + 4, bdata, bsize);
|
||||
} else {
|
||||
memcpy (data + 11 + 1, bdata, bsize);
|
||||
|
@ -1083,8 +1096,9 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
|
|||
|
||||
GST_WRITE_UINT32_BE (data + size - 4, size - 4);
|
||||
|
||||
GST_BUFFER_TIMESTAMP (tag) = GST_BUFFER_TIMESTAMP (buffer);
|
||||
GST_BUFFER_DURATION (tag) = GST_BUFFER_DURATION (buffer);
|
||||
GST_BUFFER_PTS (tag) = GST_CLOCK_TIME_NONE;
|
||||
GST_BUFFER_DTS (tag) = GST_CLOCK_TIME_NONE;
|
||||
GST_BUFFER_DURATION (tag) = GST_CLOCK_TIME_NONE;
|
||||
GST_BUFFER_OFFSET (tag) = GST_BUFFER_OFFSET (buffer);
|
||||
GST_BUFFER_OFFSET_END (tag) = GST_BUFFER_OFFSET_END (buffer);
|
||||
|
||||
|
@ -1267,11 +1281,10 @@ gst_flv_mux_update_index (GstFlvMux * mux, GstBuffer * buffer, GstFlvPad * cpad)
|
|||
GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)))
|
||||
return;
|
||||
|
||||
if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
|
||||
if (GST_BUFFER_PTS_IS_VALID (buffer)) {
|
||||
GstFlvMuxIndexEntry *entry = g_slice_new (GstFlvMuxIndexEntry);
|
||||
entry->position = mux->byte_count;
|
||||
entry->time =
|
||||
gst_guint64_to_gdouble (GST_BUFFER_TIMESTAMP (buffer)) / GST_SECOND;
|
||||
entry->time = gst_guint64_to_gdouble (GST_BUFFER_PTS (buffer)) / GST_SECOND;
|
||||
mux->index = g_list_prepend (mux->index, entry);
|
||||
}
|
||||
}
|
||||
|
@ -1293,8 +1306,8 @@ gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvPad * cpad, GstBuffer * buffer)
|
|||
|
||||
ret = gst_flv_mux_push (mux, tag);
|
||||
|
||||
if (ret == GST_FLOW_OK && GST_BUFFER_TIMESTAMP_IS_VALID (tag))
|
||||
cpad->last_timestamp = GST_BUFFER_TIMESTAMP (tag);
|
||||
if (ret == GST_FLOW_OK && GST_BUFFER_DTS_IS_VALID (tag))
|
||||
cpad->last_timestamp = GST_BUFFER_DTS (tag);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1492,7 +1505,7 @@ gst_flv_mux_handle_buffer (GstCollectPads * pads, GstCollectData * cdata,
|
|||
best = (GstFlvPad *) cdata;
|
||||
if (best) {
|
||||
g_assert (buffer);
|
||||
best_time = GST_BUFFER_TIMESTAMP (buffer);
|
||||
best_time = GST_BUFFER_DTS (buffer);
|
||||
} else {
|
||||
best_time = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue