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 */ /* 1 byte of tag type + 3 bytes of tag data size */
#define FLV_TAG_TYPE_SIZE 4 #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 #define RESYNC_THRESHOLD 2000
static gboolean flv_demux_handle_seek_push (GstFlvDemux * demux, static gboolean flv_demux_handle_seek_push (GstFlvDemux * demux,
@ -889,23 +889,23 @@ gst_flv_demux_push_tags (GstFlvDemux * demux)
} }
static gboolean 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) guint32 * last, GstClockTime * offset)
{ {
gboolean ret = FALSE; gboolean ret = FALSE;
gint32 dpts = pts - *last; gint32 ddts = dts - *last;
if (!discont && ABS (dpts) >= RESYNC_THRESHOLD) { if (!discont && ABS (ddts) >= RESYNC_THRESHOLD) {
/* Theoretically, we should use substract the duration of the last buffer, /* Theoretically, we should use substract the duration of the last buffer,
but this demuxer sends no durations on buffers, not sure if it cannot but this demuxer sends no durations on buffers, not sure if it cannot
know, or just does not care to calculate. */ know, or just does not care to calculate. */
*offset -= dpts * GST_MSECOND; *offset -= ddts * GST_MSECOND;
GST_WARNING_OBJECT (demux, GST_WARNING_OBJECT (demux,
"Large pts gap (%" G_GINT32_FORMAT " ms), assuming resync, offset now %" "Large dts gap (%" G_GINT32_FORMAT " ms), assuming resync, offset now %"
GST_TIME_FORMAT "", dpts, GST_TIME_ARGS (*offset)); GST_TIME_FORMAT "", ddts, GST_TIME_ARGS (*offset));
ret = TRUE; ret = TRUE;
} }
*last = pts; *last = dts;
return ret; return ret;
} }
@ -1113,7 +1113,8 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
} }
/* Fill buffer with data */ /* 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_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET (outbuf) = demux->audio_offset++; GST_BUFFER_OFFSET (outbuf) = demux->audio_offset++;
GST_BUFFER_OFFSET_END (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) gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
{ {
GstFlowReturn ret = GST_FLOW_OK; 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; gboolean keyframe = FALSE;
guint8 flags = 0, codec_tag = 0; guint8 flags = 0, codec_tag = 0;
GstBuffer *outbuf; GstBuffer *outbuf;
@ -1337,14 +1339,14 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
data = map.data; data = map.data;
/* Grab information about video tag */ /* Grab information about video tag */
pts = GST_READ_UINT24_BE (data); dts = GST_READ_UINT24_BE (data);
/* read the pts extension to 32 bits integer */ /* read the dts extension to 32 bits integer */
pts_ext = GST_READ_UINT8 (data + 3); dts_ext = GST_READ_UINT8 (data + 3);
/* Combine them */ /* 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], GST_LOG_OBJECT (demux, "dts bytes %02X %02X %02X %02X (%d)", data[0], data[1],
data[2], data[3], pts); data[2], data[3], dts);
/* Skip the stream id and go directly to the flags */ /* Skip the stream id and go directly to the flags */
flags = GST_READ_UINT8 (data + 7); 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) { if (codec_tag == 4 || codec_tag == 5) {
codec_data = 2; codec_data = 2;
} else if (codec_tag == 7) { } else if (codec_tag == 7) {
gint32 cts;
codec_data = 5; codec_data = 5;
cts = GST_READ_UINT24_BE (data + 9); cts = GST_READ_UINT24_BE (data + 9);
cts = (cts + 0xff800000) ^ 0xff800000; cts = (cts + 0xff800000) ^ 0xff800000;
GST_LOG_OBJECT (demux, "got cts %d", cts); 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) " 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 */ /* detect (and deem to be resyncs) large dts gaps */
if (gst_flv_demux_update_resync (demux, pts, demux->video_need_discont, if (gst_flv_demux_update_resync (demux, dts, demux->video_need_discont,
&demux->last_video_pts, &demux->video_time_offset)) { &demux->last_video_dts, &demux->video_time_offset)) {
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC);
} }
/* Fill buffer with data */ /* 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_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET (outbuf) = demux->video_offset++; GST_BUFFER_OFFSET (outbuf) = demux->video_offset++;
GST_BUFFER_OFFSET_END (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, 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 " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
", keyframe (%d)", gst_buffer_get_size (outbuf), ", 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), GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
keyframe); keyframe);
@ -1594,7 +1594,7 @@ static GstClockTime
gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index, gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
GstBuffer * buffer, size_t * tag_size) GstBuffer * buffer, size_t * tag_size)
{ {
guint32 pts = 0, pts_ext = 0; guint32 dts = 0, dts_ext = 0;
guint32 tag_data_size; guint32 tag_data_size;
guint8 type; guint8 type;
gboolean keyframe = TRUE; gboolean keyframe = TRUE;
@ -1636,15 +1636,15 @@ gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
data += 4; 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]); data[2], data[3]);
/* Grab timestamp of tag tag */ /* Grab timestamp of tag tag */
pts = GST_READ_UINT24_BE (data); dts = GST_READ_UINT24_BE (data);
/* read the pts extension to 32 bits integer */ /* read the dts extension to 32 bits integer */
pts_ext = GST_READ_UINT8 (data + 3); dts_ext = GST_READ_UINT8 (data + 3);
/* Combine them */ /* Combine them */
pts |= pts_ext << 24; dts |= dts_ext << 24;
if (type == 9) { if (type == 9) {
data += 7; data += 7;
@ -1652,8 +1652,8 @@ gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
keyframe = ((data[0] >> 4) == 1); keyframe = ((data[0] >> 4) == 1);
} }
ret = pts * GST_MSECOND; ret = dts * GST_MSECOND;
GST_LOG_OBJECT (demux, "pts: %" GST_TIME_FORMAT, GST_TIME_ARGS (ret)); GST_LOG_OBJECT (demux, "dts: %" GST_TIME_FORMAT, GST_TIME_ARGS (ret));
if (index && !demux->indexed && (type == 9 || (type == 8 if (index && !demux->indexed && (type == 9 || (type == 8
&& !demux->has_video))) { && !demux->has_video))) {
@ -1823,7 +1823,7 @@ gst_flv_demux_cleanup (GstFlvDemux * demux)
demux->index_max_time = 0; demux->index_max_time = 0;
demux->audio_start = demux->video_start = GST_CLOCK_TIME_NONE; 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->audio_time_offset = demux->video_time_offset = 0;
demux->no_more_pads = FALSE; demux->no_more_pads = FALSE;

View file

@ -118,7 +118,7 @@ struct _GstFlvDemux
gboolean got_par; gboolean got_par;
GstBuffer * video_codec_data; GstBuffer * video_codec_data;
GstClockTime video_start; GstClockTime video_start;
guint32 last_video_pts; guint32 last_video_dts;
GstClockTime video_time_offset; GstClockTime video_time_offset;
gdouble framerate; gdouble framerate;

View file

@ -1001,12 +1001,27 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
GstBuffer *tag; GstBuffer *tag;
GstMapInfo map; GstMapInfo map;
guint size; guint size;
guint32 timestamp = guint32 pts, dts, cts;
(GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) ? GST_BUFFER_TIMESTAMP (buffer) /
GST_MSECOND : cpad->last_timestamp / GST_MSECOND;
guint8 *data, *bdata; guint8 *data, *bdata;
gsize bsize; 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); gst_buffer_map (buffer, &map, GST_MAP_READ);
bdata = map.data; bdata = map.data;
bsize = map.size; bsize = map.size;
@ -1028,7 +1043,6 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
size += 4; size += 4;
_gst_buffer_new_and_alloc (size, &tag, &data); _gst_buffer_new_and_alloc (size, &tag, &data);
GST_BUFFER_TIMESTAMP (tag) = timestamp * GST_MSECOND;
memset (data, 0, size); memset (data, 0, size);
data[0] = (cpad->video) ? 9 : 8; 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[2] = ((size - 11 - 4) >> 8) & 0xff;
data[3] = ((size - 11 - 4) >> 0) & 0xff; data[3] = ((size - 11 - 4) >> 0) & 0xff;
/* wrap the timestamp every G_MAXINT32 miliseconds */ GST_WRITE_UINT24_BE (data + 4, dts);
timestamp &= 0x7fffffff; data[7] = (((guint) dts) >> 24) & 0xff;
data[4] = (timestamp >> 16) & 0xff;
data[5] = (timestamp >> 8) & 0xff;
data[6] = (timestamp >> 0) & 0xff;
data[7] = (timestamp >> 24) & 0xff;
data[8] = data[9] = data[10] = 0; 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; data[11] |= cpad->video_codec & 0x0f;
if (cpad->video_codec == 7) { if (cpad->video_codec == 7) {
data[12] = is_codec_data ? 0 : 1; if (is_codec_data) {
data[12] = 0;
/* FIXME: what to do about composition time */ GST_WRITE_UINT24_BE (data + 13, 0);
data[13] = data[14] = data[15] = 0; } else {
/* ACV NALU */
data[12] = 1;
GST_WRITE_UINT24_BE (data + 13, cts);
}
memcpy (data + 11 + 1 + 4, bdata, bsize); memcpy (data + 11 + 1 + 4, bdata, bsize);
} else { } else {
memcpy (data + 11 + 1, bdata, bsize); 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_WRITE_UINT32_BE (data + size - 4, size - 4);
GST_BUFFER_TIMESTAMP (tag) = GST_BUFFER_TIMESTAMP (buffer); GST_BUFFER_PTS (tag) = GST_CLOCK_TIME_NONE;
GST_BUFFER_DURATION (tag) = GST_BUFFER_DURATION (buffer); 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 (tag) = GST_BUFFER_OFFSET (buffer);
GST_BUFFER_OFFSET_END (tag) = GST_BUFFER_OFFSET_END (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))) GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)))
return; return;
if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { if (GST_BUFFER_PTS_IS_VALID (buffer)) {
GstFlvMuxIndexEntry *entry = g_slice_new (GstFlvMuxIndexEntry); GstFlvMuxIndexEntry *entry = g_slice_new (GstFlvMuxIndexEntry);
entry->position = mux->byte_count; entry->position = mux->byte_count;
entry->time = entry->time = gst_guint64_to_gdouble (GST_BUFFER_PTS (buffer)) / GST_SECOND;
gst_guint64_to_gdouble (GST_BUFFER_TIMESTAMP (buffer)) / GST_SECOND;
mux->index = g_list_prepend (mux->index, entry); 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); ret = gst_flv_mux_push (mux, tag);
if (ret == GST_FLOW_OK && GST_BUFFER_TIMESTAMP_IS_VALID (tag)) if (ret == GST_FLOW_OK && GST_BUFFER_DTS_IS_VALID (tag))
cpad->last_timestamp = GST_BUFFER_TIMESTAMP (tag); cpad->last_timestamp = GST_BUFFER_DTS (tag);
return ret; return ret;
} }
@ -1492,7 +1505,7 @@ gst_flv_mux_handle_buffer (GstCollectPads * pads, GstCollectData * cdata,
best = (GstFlvPad *) cdata; best = (GstFlvPad *) cdata;
if (best) { if (best) {
g_assert (buffer); g_assert (buffer);
best_time = GST_BUFFER_TIMESTAMP (buffer); best_time = GST_BUFFER_DTS (buffer);
} else { } else {
best_time = GST_CLOCK_TIME_NONE; best_time = GST_CLOCK_TIME_NONE;
} }