pad-monitor: Reliably track pending seeks

Instead of overriding all values when receiving a seek, store
them as a list of expected values.

This allows handling several seeks in a row, like non-flushing
seeks.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-devtools/-/merge_requests/174>
This commit is contained in:
Thibault Saunier 2020-04-30 12:39:44 -04:00
parent ec2a139246
commit 1e8a0dac2a
2 changed files with 131 additions and 118 deletions

View file

@ -120,6 +120,17 @@ G_STMT_START { \
} \
} G_STMT_END
/* Structure used to store all seek-related information */
struct _GstValidatePadSeekData
{
guint32 seqnum;
gdouble rate;
GstFormat format;
GstSeekFlags flags;
GstSeekType start_type, stop_type;
gint64 start, stop;
};
typedef struct
{
GstClockTime timestamp;
@ -874,6 +885,26 @@ gst_validate_pad_monitor_check_late_serialized_events (GstValidatePadMonitor *
gst_object_unref (pad);
}
static void
seek_data_free (GstValidatePadSeekData * data)
{
g_slice_free (GstValidatePadSeekData, data);
}
static GstValidatePadSeekData *
seek_data_for_seqnum (GstValidatePadMonitor * monitor, guint32 seqnum)
{
GList *tmp;
for (tmp = monitor->seeks; tmp; tmp = tmp->next) {
GstValidatePadSeekData *data = (GstValidatePadSeekData *) tmp->data;
if (data->seqnum == seqnum)
return data;
}
return NULL;
}
static void
gst_validate_pad_monitor_dispose (GObject * object)
{
@ -899,6 +930,8 @@ gst_validate_pad_monitor_dispose (GObject * object)
gst_caps_replace (&monitor->last_query_res, NULL);
gst_caps_replace (&monitor->last_query_filter, NULL);
g_list_free_full (monitor->seeks, (GDestroyNotify) seek_data_free);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@ -967,12 +1000,14 @@ gst_validate_pad_monitor_reset (GstValidatePadMonitor * pad_monitor)
pad_monitor->pending_newsegment_seqnum = GST_SEQNUM_INVALID;
pad_monitor->pending_eos_seqnum = GST_SEQNUM_INVALID;
pad_monitor->pending_seek_accurate_time = GST_CLOCK_TIME_NONE;
if (pad_monitor->pending_setcaps_fields)
gst_structure_free (pad_monitor->pending_setcaps_fields);
pad_monitor->pending_setcaps_fields =
gst_structure_new_empty (PENDING_FIELDS);
if (pad_monitor->seeks)
g_list_free_full (pad_monitor->seeks, (GDestroyNotify) seek_data_free);
pad_monitor->current_seek = NULL;
pad_monitor->seeks = NULL;
/* FIXME : Why BYTES and not UNDEFINED ? */
gst_segment_init (&pad_monitor->segment, GST_FORMAT_BYTES);
@ -1684,13 +1719,19 @@ gst_validate_pad_monitor_common_event_check (GstValidatePadMonitor *
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_START:
{
if (pad_monitor->pending_flush_start_seqnum != GST_SEQNUM_INVALID) {
if (seqnum == pad_monitor->pending_flush_start_seqnum) {
pad_monitor->pending_flush_start_seqnum = GST_SEQNUM_INVALID;
} else {
if (pad_monitor->seeks) {
GstValidatePadSeekData *seekdata =
seek_data_for_seqnum (pad_monitor, seqnum);
if (!seekdata)
GST_VALIDATE_REPORT (pad_monitor, FLUSH_START_HAS_WRONG_SEQNUM,
"Got: %u Expected: %u", seqnum,
pad_monitor->pending_flush_start_seqnum);
"Got: %" G_GUINT32_FORMAT " Expected: %" G_GUINT32_FORMAT, seqnum,
((GstValidatePadSeekData *) pad_monitor->seeks->data)->seqnum);
else {
if (!(seekdata->flags & GST_SEEK_FLAG_FLUSH)) {
GST_VALIDATE_REPORT (pad_monitor, EVENT_FLUSH_START_UNEXPECTED,
"Received flush-start for a non-flushing seek");
}
}
}
@ -1699,18 +1740,21 @@ gst_validate_pad_monitor_common_event_check (GstValidatePadMonitor *
"Received flush-start from when flush-stop was expected");
}
pad_monitor->pending_flush_stop = TRUE;
/* Remove the current segment seekdata */
if (pad_monitor->current_seek) {
pad_monitor->seeks =
g_list_remove (pad_monitor->seeks, pad_monitor->current_seek);
seek_data_free (pad_monitor->current_seek);
pad_monitor->current_seek = NULL;
}
}
break;
case GST_EVENT_FLUSH_STOP:
{
if (pad_monitor->pending_flush_stop_seqnum != GST_SEQNUM_INVALID) {
if (seqnum == pad_monitor->pending_flush_stop_seqnum) {
pad_monitor->pending_flush_stop_seqnum = GST_SEQNUM_INVALID;
} else {
GST_VALIDATE_REPORT (pad_monitor, FLUSH_STOP_HAS_WRONG_SEQNUM,
"Got: %u Expected: %u", seqnum,
pad_monitor->pending_flush_stop_seqnum);
}
if (pad_monitor->seeks && !seek_data_for_seqnum (pad_monitor, seqnum)) {
GST_VALIDATE_REPORT (pad_monitor, FLUSH_STOP_HAS_WRONG_SEQNUM,
"Got: %" G_GUINT32_FORMAT " Expected: %" G_GUINT32_FORMAT, seqnum,
((GstValidatePadSeekData *) pad_monitor->seeks->data)->seqnum);
}
pad_monitor->pending_newsegment_seqnum = seqnum;
@ -1897,31 +1941,34 @@ gst_validate_pad_monitor_downstream_event_check (GstValidatePadMonitor *
pad_monitor->pending_buffer_discont = TRUE;
break;
case GST_EVENT_SEGMENT:
{
GstValidatePadSeekData *seekdata =
seek_data_for_seqnum (pad_monitor, seqnum);
/* parse segment data to be used if event is handled */
gst_event_parse_segment (event, &segment);
GST_DEBUG_OBJECT (pad, "Got segment %" GST_SEGMENT_FORMAT, segment);
/* Reset expected flush start/stop values, we have a segment */
pad_monitor->pending_flush_start_seqnum = GST_SEQNUM_INVALID;
pad_monitor->pending_flush_stop_seqnum = GST_SEQNUM_INVALID;
GST_DEBUG_OBJECT (pad,
"Got segment seqnum:%" G_GUINT32_FORMAT " %" GST_SEGMENT_FORMAT,
seqnum, segment);
if (pad_monitor->pending_newsegment_seqnum != GST_SEQNUM_INVALID) {
if (pad_monitor->pending_newsegment_seqnum == seqnum) {
pad_monitor->pending_newsegment_seqnum = GST_SEQNUM_INVALID;
if (GST_CLOCK_TIME_IS_VALID (pad_monitor->pending_seek_accurate_time)) {
if (segment->time == pad_monitor->pending_seek_accurate_time) {
pad_monitor->pending_seek_accurate_time = GST_CLOCK_TIME_NONE;
} else {
GST_VALIDATE_REPORT (pad_monitor, SEGMENT_HAS_WRONG_START,
"After an accurate seek, got: %" GST_TIME_FORMAT
" Expected: %" GST_TIME_FORMAT, GST_TIME_ARGS (segment->time),
GST_TIME_ARGS (pad_monitor->pending_seek_accurate_time));
}
}
} else {
/* FIXME: Convert to more robust checks */
if (pad_monitor->pending_newsegment_seqnum != seqnum) {
GST_VALIDATE_REPORT (pad_monitor, SEGMENT_HAS_WRONG_SEQNUM,
"Got: %u Expected: %u", seqnum, pad_monitor->pending_eos_seqnum);
"Got: %u Expected: %u", seqnum,
pad_monitor->pending_newsegment_seqnum);
}
}
if (seekdata && seekdata != pad_monitor->current_seek) {
/* Check for accurate seeks */
if (seekdata->flags & GST_SEEK_FLAG_ACCURATE) {
if (segment->time != seekdata->start)
GST_VALIDATE_REPORT (pad_monitor, SEGMENT_HAS_WRONG_START,
"After an accurate seek, got: %" GST_TIME_FORMAT
" Expected: %" GST_TIME_FORMAT, GST_TIME_ARGS (segment->time),
GST_TIME_ARGS (seekdata->start));
}
}
@ -1964,6 +2011,19 @@ gst_validate_pad_monitor_downstream_event_check (GstValidatePadMonitor *
gst_event_replace (&pad_monitor->expected_segment, NULL);
}
}
/* Drop all expected seekdata from before this segment */
if (seekdata) {
while (pad_monitor->seeks && pad_monitor->seeks->data != seekdata) {
GstValidatePadSeekData *tmp =
(GstValidatePadSeekData *) pad_monitor->seeks->data;
pad_monitor->seeks =
g_list_delete_link (pad_monitor->seeks, pad_monitor->seeks);
seek_data_free (tmp);
}
}
pad_monitor->current_seek = seekdata;
}
break;
case GST_EVENT_CAPS:{
GstCaps *caps;
@ -2056,100 +2116,52 @@ gst_validate_pad_monitor_downstream_event_check (GstValidatePadMonitor *
return ret;
}
static GstValidatePadSeekData *
_store_seek_event_data (GstValidatePadMonitor * pad_monitor, GstEvent * event)
{
GstValidatePadSeekData *data = g_slice_new0 (GstValidatePadSeekData);
data->seqnum = gst_event_get_seqnum (event);
gst_event_parse_seek (event, &data->rate, &data->format, &data->flags,
&data->start_type, &data->start, &data->stop_type, &data->stop);
pad_monitor->seeks = g_list_append (pad_monitor->seeks, data);
return data;
}
static gboolean
gst_validate_pad_monitor_src_event_check (GstValidatePadMonitor * pad_monitor,
GstObject * parent, GstEvent * event, GstPadEventFunction handler)
{
gboolean ret = TRUE;
gdouble rate;
GstFormat format;
gint64 start, stop;
GstSeekFlags seek_flags;
GstSeekType start_type, stop_type;
guint32 seqnum = gst_event_get_seqnum (event);
GstPad *pad =
GST_PAD (gst_validate_monitor_get_target (GST_VALIDATE_MONITOR
(pad_monitor)));
gst_validate_pad_monitor_common_event_check (pad_monitor, event);
/* pre checks */
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
{
gst_event_parse_seek (event, &rate, &format, &seek_flags, &start_type,
&start, &stop_type, &stop);
/* upstream seek - store the seek event seqnum to check
* flushes and newsegments share the same */
}
break;
/* both flushes are handled by the common event handling function */
case GST_EVENT_FLUSH_START:
case GST_EVENT_FLUSH_STOP:
case GST_EVENT_NAVIGATION:
case GST_EVENT_LATENCY:
case GST_EVENT_STEP:
case GST_EVENT_QOS:
default:
break;
}
if (handler) {
GST_VALIDATE_MONITOR_UNLOCK (pad_monitor);
/* Safely store pending accurate seek values */
if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
if (seek_flags & GST_SEEK_FLAG_ACCURATE && format == GST_FORMAT_TIME) {
GST_DEBUG_OBJECT (pad,
"Storing expected accurate seek time %" GST_TIME_FORMAT,
GST_TIME_ARGS (start));
pad_monitor->pending_seek_accurate_time = start;
}
/* TODO we might need to use a list as multiple seeks can be sent
* before the flushes arrive here */
if (seek_flags & GST_SEEK_FLAG_FLUSH) {
pad_monitor->pending_flush_start_seqnum = seqnum;
pad_monitor->pending_flush_stop_seqnum = seqnum;
}
}
GstValidatePadSeekData *seekdata = NULL;
gst_event_ref (event);
GST_DEBUG_OBJECT (pad, "event %" GST_PTR_FORMAT, event);
/* Safely store pending accurate seek values */
if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK)
seekdata = _store_seek_event_data (pad_monitor, event);
GST_VALIDATE_MONITOR_UNLOCK (pad_monitor);
ret = pad_monitor->event_func (pad, parent, event);
if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
/* If the seek was already handled (same current seqnum), reset the
* expected accurate seek value */
if (ret && pad_monitor->has_segment
&& seqnum == pad_monitor->pending_eos_seqnum) {
GST_DEBUG_OBJECT (pad,
"Resetting expected accurate seek value, was already handled");
pad_monitor->pending_seek_accurate_time = GST_CLOCK_TIME_NONE;
} else if (!ret) {
/* do not expect any of these events anymore */
pad_monitor->pending_flush_start_seqnum = GST_SEQNUM_INVALID;
pad_monitor->pending_flush_stop_seqnum = GST_SEQNUM_INVALID;
pad_monitor->pending_newsegment_seqnum = GST_SEQNUM_INVALID;
pad_monitor->pending_eos_seqnum = GST_SEQNUM_INVALID;
pad_monitor->pending_seek_accurate_time = GST_CLOCK_TIME_NONE;
}
}
GST_VALIDATE_MONITOR_LOCK (pad_monitor);
if (seekdata && !ret) {
/* Remove failed seek from list */
GST_LOG_OBJECT (pad, "Failed seek, removing stored seek data");
pad_monitor->seeks = g_list_remove (pad_monitor->seeks, seekdata);
g_slice_free (GstValidatePadSeekData, seekdata);
}
}
/* post checks */
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_START:
case GST_EVENT_FLUSH_STOP:
case GST_EVENT_QOS:
case GST_EVENT_SEEK:
case GST_EVENT_NAVIGATION:
case GST_EVENT_LATENCY:
case GST_EVENT_STEP:
default:
break;
}
if (handler)
gst_event_unref (event);
gst_object_unref (pad);
return ret;
}
@ -2637,11 +2649,13 @@ gst_validate_pad_monitor_event_probe (GstPad * pad, GstEvent * event,
gpointer udata)
{
GstValidatePadMonitor *monitor = GST_VALIDATE_PAD_MONITOR_CAST (udata);
guint32 seqnum = gst_event_get_seqnum (event);
GST_VALIDATE_PAD_MONITOR_PARENT_LOCK (monitor);
GST_VALIDATE_MONITOR_LOCK (monitor);
GST_DEBUG_OBJECT (pad, "event %p %s", event, GST_EVENT_TYPE_NAME (event));
GST_DEBUG_OBJECT (pad, "event %p %s seqnum:%" G_GUINT32_FORMAT, event,
GST_EVENT_TYPE_NAME (event), seqnum);
if (GST_EVENT_IS_SERIALIZED (event)) {
gint i;

View file

@ -27,6 +27,7 @@
typedef struct _GstValidatePadMonitor GstValidatePadMonitor;
typedef struct _GstValidatePadMonitorClass GstValidatePadMonitorClass;
typedef struct _GstValidatePadSeekData GstValidatePadSeekData;
#include <gst/validate/gst-validate-monitor.h>
#include <gst/validate/media-descriptor-parser.h>
@ -54,8 +55,6 @@ G_BEGIN_DECLS
struct _GstValidatePadMonitor {
GstValidateMonitor parent;
GstValidateElementMonitor *element_monitor;
gboolean setup;
GstPadChainFunction chain_func;
@ -81,17 +80,17 @@ struct _GstValidatePadMonitor {
gboolean is_eos;
gboolean pending_flush_stop;
guint32 pending_flush_stop_seqnum;
guint32 pending_flush_start_seqnum;
guint32 pending_newsegment_seqnum;
guint32 pending_eos_seqnum;
/* List of GstValidatePadSeekData containing pending/current seeks */
GList *seeks;
GstValidatePadSeekData *current_seek;
/* Whether the next buffer should have a DISCONT flag on it, because
* it's the first one, or follows a SEGMENT and/or a FLUSH */
gboolean pending_buffer_discont;
GstClockTime pending_seek_accurate_time;
GstEvent *expected_segment;
GPtrArray *serialized_events;
GList *expired_events;