Commit dfabc5b0 authored by Tim-Philipp Müller's avatar Tim-Philipp Müller 🐠 Committed by Tim-Philipp Müller
Browse files

asfdemux: scan last packet to determine duration for files saved from broadcasts

ASF files that were saved from broadcast may not have a duration, and
most certainly not a seek table, so try to scan the last packet and
determine an approximate duration based on the send time of the first
and last packet. Seeking still needs fixing though.

gstreamer/gst-plugins-ugly#4
parent b081d5a3
Pipeline #38696 failed with stages
in 28 minutes and 15 seconds
......@@ -27,6 +27,7 @@
#include <gst/gstutils.h>
#include <gst/gstinfo.h>
#include <gst/base/gstbytereader.h>
#include <string.h>
#define GST_ASF_PAYLOAD_KF_COMPLETE(stream, payload) (stream->is_video && payload->keyframe && payload->buf_filled >= payload->mo_size)
......@@ -806,3 +807,61 @@ done:
gst_buffer_unmap (buf, &map);
return ret;
}
gboolean
gst_asf_packet_get_packet_times (GstASFDemux * demux, GstBuffer * buf,
GstClockTime * send_time, GstClockTime * duration)
{
static const guint lens[4] = { 0, 1, 2, 4 };
GstByteReader br;
GstMapInfo map;
gboolean ret = FALSE;
guint8 ec_flags, flags1, len;
gst_buffer_map (buf, &map, GST_MAP_READ);
gst_byte_reader_init (&br, map.data, map.size);
/* need at least two payload flag bytes, send time, and duration */
if (gst_byte_reader_get_remaining (&br) < 1 + 1 + 4 + 2)
goto done;
ec_flags = gst_byte_reader_get_uint8_unchecked (&br);
/* skip optional error correction stuff */
if ((ec_flags & 0x80) != 0) {
guint ec_len_type, ec_len;
ec_len_type = (ec_flags & 0x60) >> 5;
if (ec_len_type == 0) {
ec_len = ec_flags & 0x0f;
} else {
ec_len = 2;
}
if (!gst_byte_reader_skip (&br, ec_len))
goto done;
}
/* parse payload info */
if (!gst_byte_reader_get_uint8 (&br, &flags1) ||
!gst_byte_reader_skip (&br, 1))
goto done;
len = lens[(flags1 >> 1) & 0x03]; /* sequence len */
len += lens[(flags1 >> 3) & 0x03]; /* padding len */
len += lens[(flags1 >> 5) & 0x03]; /* length len */
if (!gst_byte_reader_skip (&br, len))
goto done;
if (gst_byte_reader_get_remaining (&br) < 4 + 2)
goto done;
*send_time = gst_byte_reader_get_uint32_le_unchecked (&br) * GST_MSECOND;
*duration = gst_byte_reader_get_uint16_le_unchecked (&br) * GST_MSECOND;
ret = TRUE;
done:
gst_buffer_unmap (buf, &map);
return ret;
}
......@@ -65,6 +65,11 @@ typedef enum {
GstAsfDemuxParsePacketError gst_asf_demux_parse_packet (GstASFDemux * demux, GstBuffer * buf);
gboolean gst_asf_packet_get_packet_times (GstASFDemux * demux,
GstBuffer * buf,
GstClockTime * send_time,
GstClockTime * duration);
#define gst_asf_payload_is_complete(payload) \
((payload)->buf_filled >= (payload)->mo_size)
......
......@@ -1128,6 +1128,73 @@ gst_asf_demux_pull_indices (GstASFDemux * demux)
return ret;
}
static gboolean
gst_asf_demux_scan_peek_packet_times (GstASFDemux * demux, guint n,
GstClockTime * send_time, GstClockTime * duration)
{
GstBuffer *buf = NULL;
if (!gst_asf_demux_pull_data (demux,
demux->data_offset + (n * demux->packet_size), demux->packet_size,
&buf, NULL))
return FALSE;
if (!gst_asf_packet_get_packet_times (demux, buf, send_time, duration)) {
GST_WARNING_OBJECT (demux, "Couldn't extract send time from packet %u", n);
gst_buffer_unref (buf);
return FALSE;
}
GST_INFO_OBJECT (demux, "packet %u: send time %" GST_TIME_FORMAT
", duration %" GST_TIME_FORMAT, n, GST_TIME_ARGS (*send_time),
GST_TIME_ARGS (*duration));
gst_buffer_unref (buf);
return TRUE;
}
static gboolean
gst_asf_demux_scan_for_duration (GstASFDemux * demux)
{
GstClockTime start, stop, duration;
gint64 size = 0;
guint num_packets, last;
if (!gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &size))
return FALSE;
GST_INFO_OBJECT (demux, "file size %" G_GINT64_FORMAT, size);
GST_INFO_OBJECT (demux, "data offset %" G_GUINT64_FORMAT, demux->data_offset);
GST_INFO_OBJECT (demux, "packet size %u", demux->packet_size);
if (size <= demux->data_offset)
return FALSE;
num_packets = (size - demux->data_offset) / demux->packet_size;
GST_INFO_OBJECT (demux, "num_packets = %u (calculated)", num_packets);
if (num_packets < 10) /* arbitrary value */
return FALSE;
if (!gst_asf_demux_scan_peek_packet_times (demux, 0, &start, &duration))
return FALSE;
last = num_packets - 1;
if (!gst_asf_demux_scan_peek_packet_times (demux, last, &stop, &duration))
return FALSE;
if (GST_CLOCK_TIME_IS_VALID (duration))
stop += duration;
demux->num_packets = num_packets;
demux->play_time = stop - start;
demux->segment.duration = stop - start;
GST_INFO_OBJECT (demux, "scanned duration: %" GST_TIME_FORMAT,
GST_TIME_ARGS (demux->play_time));
return TRUE;
}
static gboolean
gst_asf_demux_parse_data_object_start (GstASFDemux * demux, guint8 * data)
{
......@@ -1160,6 +1227,11 @@ gst_asf_demux_parse_data_object_start (GstASFDemux * demux, guint8 * data)
demux->num_packets = GST_READ_UINT64_LE (data + (16 + 8) + 16);
} else {
demux->num_packets = 0;
if (!demux->streaming) {
if (!gst_asf_demux_scan_for_duration (demux))
GST_WARNING_OBJECT (demux, "couldn't determine duration");
}
}
if (demux->num_packets == 0)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment