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:
Nicolas Dufresne 2014-06-06 23:17:52 -04:00 committed by Nicolas Dufresne
parent a98341397d
commit ff2bce7b26
3 changed files with 74 additions and 61 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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;
}