Merge branch 'asfdemux-scan-duration' into 'main'

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

Closes #2633

See merge request gstreamer/gstreamer!1778
This commit is contained in:
Tim-Philipp Müller 2024-05-03 20:23:11 +00:00
commit d605330bd5
3 changed files with 136 additions and 0 deletions

View file

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

View file

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

View file

@ -1150,6 +1150,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)
{
@ -1182,6 +1249,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)