Merging gst-devtools
[platform/upstream/gstreamer.git] / validate / gst / validate / gst-validate-pad-monitor.c
index 3dbcb97..7cf92fc 100644 (file)
@@ -30,6 +30,8 @@
 #include "gst-validate-element-monitor.h"
 #include "gst-validate-pipeline-monitor.h"
 #include "gst-validate-reporter.h"
+#include "gst-validate-utils.h"
+#include "validate.h"
 #include <string.h>
 #include <stdarg.h>
 
@@ -39,6 +41,8 @@
  *
  * TODO
  */
+#define _GET_PAD_MONITOR(p) g_object_get_qdata ((GObject*) p, _Q_VALIDATE_MONITOR)
+#define _SET_PAD_MONITOR(p,d) g_object_set_qdata ((GObject*) p, _Q_VALIDATE_MONITOR, d)
 
 static GstValidateInterceptionReturn
 gst_validate_pad_monitor_intercept_report (GstValidateReporter * reporter,
@@ -78,6 +82,12 @@ G_DEFINE_TYPE_WITH_CODE (GstValidatePadMonitor, gst_validate_pad_monitor,
             GST_VALIDATE_MONITOR_GET_PARENT(m)) : \
         FALSE)
 
+#define PAD_PARENT_IS_SINK(m) \
+    (GST_VALIDATE_MONITOR_GET_PARENT(m) ? \
+        GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_SINK ( \
+            GST_VALIDATE_MONITOR_GET_PARENT(m)) : \
+        FALSE)
+
 
 /*
  * Locking the parent should always be done before locking the
@@ -116,6 +126,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;
@@ -125,21 +146,25 @@ typedef struct
 static GstPad *
 _get_actual_pad (GstPad * pad)
 {
-  GstPad *tmp_pad;
+  pad = gst_object_ref (pad);
 
-  gst_object_ref (pad);
+  while (GST_IS_PROXY_PAD (pad)) {
+    GstPad *next_pad;
 
-  /* We don't monitor ghost pads */
-  while (GST_IS_GHOST_PAD (pad)) {
-    tmp_pad = pad;
-    pad = gst_ghost_pad_get_target ((GstGhostPad *) pad);
-    gst_object_unref (tmp_pad);
-  }
+    if (GST_PAD_IS_SINK (pad)) {
+      if (GST_IS_GHOST_PAD (pad))
+        next_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
+      else
+        next_pad = GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
+    } else {
+      next_pad = gst_pad_get_peer (pad);
+    }
 
-  while (GST_IS_PROXY_PAD (pad)) {
-    tmp_pad = pad;
-    pad = gst_pad_get_peer (pad);
-    gst_object_unref (tmp_pad);
+    gst_object_unref (pad);
+    if (!next_pad)
+      return NULL;
+
+    pad = next_pad;
   }
 
   return pad;
@@ -160,7 +185,7 @@ _find_master_report_on_pad (GstPad * pad, GstValidateReport * report)
     return FALSE;
   }
 
-  pad_monitor = g_object_get_data ((GObject *) pad, "validate-monitor");
+  pad_monitor = _GET_PAD_MONITOR (pad);
 
   /* For some reason this pad isn't monitored */
   if (pad_monitor == NULL)
@@ -528,7 +553,7 @@ _structure_is_video (GstStructure * structure)
   const gchar *name = gst_structure_get_name (structure);
 
   return g_strstr_len (name, 6, "video/")
-      && strcmp (name, "video/quicktime") != 0;
+      && g_strcmp0 (name, "video/quicktime") != 0;
 }
 
 static gboolean
@@ -867,6 +892,26 @@ gst_validate_pad_monitor_check_late_serialized_events (GstValidatePadMonitor *
 }
 
 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)
 {
   GstValidatePadMonitor *monitor = GST_VALIDATE_PAD_MONITOR_CAST (object);
@@ -890,6 +935,9 @@ gst_validate_pad_monitor_dispose (GObject * object)
   gst_caps_replace (&monitor->last_caps, NULL);
   gst_caps_replace (&monitor->last_query_res, NULL);
   gst_caps_replace (&monitor->last_query_filter, NULL);
+  gst_caps_replace (&monitor->last_refused_caps, NULL);
+
+  g_list_free_full (monitor->seeks, (GDestroyNotify) seek_data_free);
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
@@ -938,8 +986,6 @@ gst_validate_pad_monitor_flush (GstValidatePadMonitor * pad_monitor)
   pad_monitor->current_timestamp = GST_CLOCK_TIME_NONE;
   pad_monitor->current_duration = GST_CLOCK_TIME_NONE;
 
-  pad_monitor->last_flow_return = GST_FLOW_OK;
-
   pad_monitor->timestamp_range_start = GST_CLOCK_TIME_NONE;
   pad_monitor->timestamp_range_end = GST_CLOCK_TIME_NONE;
 }
@@ -951,7 +997,7 @@ gst_validate_pad_monitor_reset (GstValidatePadMonitor * pad_monitor)
 {
   gst_validate_pad_monitor_flush (pad_monitor);
 
-  /* Note : For the entries that haven't been resetted in _flush(), do
+  /* Note : For the entries that haven't been reset in _flush(), do
    * it here and keep in the same order as the GstValidatePadMonitor
    * structure */
 
@@ -959,15 +1005,24 @@ 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);
+
+  pad_monitor->min_buf_freq = 0;
+  pad_monitor->buffers_pushed = 0;
+  pad_monitor->last_buffers_pushed = 0;
+  pad_monitor->min_buf_freq_interval_ts = GST_CLOCK_TIME_NONE;
+  pad_monitor->min_buf_freq_first_buffer_ts = GST_CLOCK_TIME_NONE;
+  pad_monitor->min_buf_freq_start = GST_CLOCK_TIME_NONE;
 }
 
 static void
@@ -1172,8 +1227,10 @@ static void
         otherpad = g_value_get_object (&value);
         GST_DEBUG_OBJECT (pad, "Checking pad %s:%s input timestamps",
             GST_DEBUG_PAD_NAME (otherpad));
-        othermonitor =
-            g_object_get_data ((GObject *) otherpad, "validate-monitor");
+        othermonitor = _GET_PAD_MONITOR (otherpad);
+        if (!othermonitor)
+          continue;
+
         GST_VALIDATE_MONITOR_LOCK (othermonitor);
         if (gst_validate_pad_monitor_timestamp_is_in_received_range
             (othermonitor, ts, tolerance)
@@ -1328,7 +1385,7 @@ gst_validate_pad_monitor_check_aggregated_return (GstValidatePadMonitor *
   gboolean done;
   GstPad *otherpad;
   GstPad *peerpad;
-  GstValidatePadMonitor *othermonitor;
+  GstState state, pending;
   GstFlowReturn aggregated = GST_FLOW_NOT_LINKED;
   gboolean found_a_pad = FALSE;
   GstPad *pad =
@@ -1344,15 +1401,10 @@ gst_validate_pad_monitor_check_aggregated_return (GstValidatePadMonitor *
         otherpad = g_value_get_object (&value);
         peerpad = gst_pad_get_peer (otherpad);
         if (peerpad) {
-          othermonitor =
-              g_object_get_data ((GObject *) peerpad, "validate-monitor");
-          if (othermonitor) {
-            found_a_pad = TRUE;
-            GST_VALIDATE_MONITOR_LOCK (othermonitor);
-            aggregated =
-                _combine_flows (aggregated, othermonitor->last_flow_return);
-            GST_VALIDATE_MONITOR_UNLOCK (othermonitor);
-          }
+          found_a_pad = TRUE;
+          aggregated =
+              _combine_flows (aggregated,
+              gst_pad_get_last_flow_return (peerpad));
 
           gst_object_unref (peerpad);
         }
@@ -1375,8 +1427,17 @@ gst_validate_pad_monitor_check_aggregated_return (GstValidatePadMonitor *
     /* no peer pad found, nothing to do */
     goto done;
   }
-  if (aggregated == GST_FLOW_OK || aggregated == GST_FLOW_EOS) {
-    GstState state, pending;
+
+  if (aggregated == GST_FLOW_FLUSHING) {
+    gst_element_get_state (GST_ELEMENT (parent), &state, &pending, 0);
+    if (state < GST_STATE_PAUSED || pending < GST_STATE_PAUSED) {
+      /* Aggregated is flushing, we might have been aggregating a combination
+       * of pads that are not what was present on the element during the actual
+       * data flow combination (pads might have been removed meanwhile) */
+
+      goto done;
+    }
+  } else if (aggregated == GST_FLOW_OK || aggregated == GST_FLOW_EOS) {
 
     /* those are acceptable situations */
     if (GST_PAD_IS_FLUSHING (pad) && ret == GST_FLOW_FLUSHING) {
@@ -1445,8 +1506,7 @@ static void
     switch (gst_iterator_next (iter, &value)) {
       case GST_ITERATOR_OK:
         otherpad = g_value_get_object (&value);
-        othermonitor =
-            g_object_get_data ((GObject *) otherpad, "validate-monitor");
+        othermonitor = _GET_PAD_MONITOR (otherpad);
         if (othermonitor) {
           SerializedEventData *data = g_slice_new0 (SerializedEventData);
           data->timestamp = last_ts;
@@ -1507,8 +1567,7 @@ gst_validate_pad_monitor_otherpad_add_pending_field (GstValidatePadMonitor *
     switch (gst_iterator_next (iter, &value)) {
       case GST_ITERATOR_OK:
         otherpad = g_value_get_object (&value);
-        othermonitor =
-            g_object_get_data ((GObject *) otherpad, "validate-monitor");
+        othermonitor = _GET_PAD_MONITOR (otherpad);
         if (othermonitor) {
           GST_VALIDATE_MONITOR_LOCK (othermonitor);
           g_assert (othermonitor->pending_setcaps_fields != NULL);
@@ -1561,8 +1620,7 @@ gst_validate_pad_monitor_otherpad_clear_pending_fields (GstValidatePadMonitor *
     switch (gst_iterator_next (iter, &value)) {
       case GST_ITERATOR_OK:
         otherpad = g_value_get_object (&value);
-        othermonitor =
-            g_object_get_data ((GObject *) otherpad, "validate-monitor");
+        othermonitor = _GET_PAD_MONITOR (otherpad);
         if (othermonitor) {
           GST_VALIDATE_MONITOR_LOCK (othermonitor);
           g_assert (othermonitor->pending_setcaps_fields != NULL);
@@ -1614,10 +1672,17 @@ gst_validate_pad_monitor_add_expected_newsegment (GstValidatePadMonitor *
     switch (gst_iterator_next (iter, &value)) {
       case GST_ITERATOR_OK:
         otherpad = g_value_get_object (&value);
-        if (!otherpad)
+        if (!otherpad) {
+          g_value_reset (&value);
           continue;
-        othermonitor =
-            g_object_get_data ((GObject *) otherpad, "validate-monitor");
+        }
+
+        othermonitor = _GET_PAD_MONITOR (otherpad);
+        if (!othermonitor) {
+          g_value_reset (&value);
+          continue;
+        }
+
         GST_VALIDATE_MONITOR_LOCK (othermonitor);
         gst_event_replace (&othermonitor->expected_segment, event);
         GST_VALIDATE_MONITOR_UNLOCK (othermonitor);
@@ -1654,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");
+          }
         }
       }
 
@@ -1669,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;
@@ -1721,8 +1795,7 @@ mark_pads_eos (GstValidatePadMonitor * pad_monitor)
   pad_monitor->is_eos = TRUE;
   if (peer) {
     real_peer = _get_actual_pad (peer);
-    peer_monitor =
-        g_object_get_data ((GObject *) real_peer, "validate-monitor");
+    peer_monitor = _GET_PAD_MONITOR (real_peer);
     if (peer_monitor)
       peer_monitor->is_eos = TRUE;
     gst_object_unref (peer);
@@ -1822,6 +1895,39 @@ gst_validate_monitor_find_next_buffer (GstValidatePadMonitor * pad_monitor)
     pad_monitor->current_buf = tmp;
 }
 
+static void
+post_segment_message (GstValidatePadMonitor * pad_monitor, GstPad * pad,
+    const GstSegment * segment, guint32 seqnum)
+{
+  GstValidateMonitor *element_monitor =
+      GST_VALIDATE_MONITOR_GET_PARENT (pad_monitor);
+  GstElement *element;
+  GstStructure *structure;
+  GstMessage *msg;
+
+  if (element_monitor == NULL)
+    return;
+
+  element = gst_validate_monitor_get_element (element_monitor);
+  if (element == NULL)
+    return;
+
+  GST_DEBUG_OBJECT (pad,
+      "Posting application message for seqnum:%" G_GUINT32_FORMAT " %"
+      GST_SEGMENT_FORMAT, seqnum, segment);
+
+  structure =
+      gst_structure_new ("validate-segment", "segment", GST_TYPE_SEGMENT,
+      segment, NULL);
+  msg = gst_message_new_application ((GstObject *) element, structure);
+  gst_message_set_seqnum (msg, seqnum);
+  gst_element_post_message (element, msg);
+
+  gst_object_unref (element);
+
+  return;
+}
+
 /* Checks whether a segment is just an update of another,
  * That is to say that only the base and offset field differ and all
  * other fields are identical */
@@ -1868,31 +1974,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));
         }
       }
 
@@ -1935,6 +2044,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;
@@ -2002,6 +2124,8 @@ gst_validate_pad_monitor_downstream_event_check (GstValidatePadMonitor *
         gst_segment_copy_into (segment, &pad_monitor->segment);
         pad_monitor->has_segment = TRUE;
         gst_validate_monitor_find_next_buffer (pad_monitor);
+        if (PAD_PARENT_IS_SINK (pad_monitor))
+          post_segment_message (pad_monitor, pad, segment, seqnum);
       }
       break;
     case GST_EVENT_CAPS:{
@@ -2027,100 +2151,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) {
-        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);
-  }
 
-  /* 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 (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);
+    }
   }
 
-  if (handler)
-    gst_event_unref (event);
   gst_object_unref (pad);
   return ret;
 }
@@ -2248,8 +2324,7 @@ static GstFlowReturn
 gst_validate_pad_monitor_chain_func (GstPad * pad, GstObject * parent,
     GstBuffer * buffer)
 {
-  GstValidatePadMonitor *pad_monitor =
-      g_object_get_data ((GObject *) pad, "validate-monitor");
+  GstValidatePadMonitor *pad_monitor = _GET_PAD_MONITOR (pad);
   GstFlowReturn ret;
 
   GST_VALIDATE_PAD_MONITOR_PARENT_LOCK (pad_monitor);
@@ -2273,7 +2348,6 @@ gst_validate_pad_monitor_chain_func (GstPad * pad, GstObject * parent,
   GST_VALIDATE_PAD_MONITOR_PARENT_LOCK (pad_monitor);
   GST_VALIDATE_MONITOR_LOCK (pad_monitor);
 
-  pad_monitor->last_flow_return = ret;
   if (ret == GST_FLOW_EOS) {
     mark_pads_eos (pad_monitor);
   }
@@ -2311,13 +2385,14 @@ static GstFlowReturn
 gst_validate_pad_monitor_sink_event_full_func (GstPad * pad, GstObject * parent,
     GstEvent * event)
 {
-  GstValidatePadMonitor *pad_monitor =
-      g_object_get_data ((GObject *) pad, "validate-monitor");
+  GstValidatePadMonitor *pad_monitor = _GET_PAD_MONITOR (pad);
   GstFlowReturn ret;
 
   GST_VALIDATE_PAD_MONITOR_PARENT_LOCK (pad_monitor);
   GST_VALIDATE_MONITOR_LOCK (pad_monitor);
 
+  GST_DEBUG_OBJECT (pad, "event %p %s", event, GST_EVENT_TYPE_NAME (event));
+
   if (gst_validate_pad_monitor_event_is_tracked (pad_monitor, event)) {
     GstClockTime last_ts = GST_CLOCK_TIME_NONE;
     if (GST_CLOCK_TIME_IS_VALID (pad_monitor->current_timestamp)) {
@@ -2353,8 +2428,7 @@ static gboolean
 gst_validate_pad_monitor_src_event_func (GstPad * pad, GstObject * parent,
     GstEvent * event)
 {
-  GstValidatePadMonitor *pad_monitor =
-      g_object_get_data ((GObject *) pad, "validate-monitor");
+  GstValidatePadMonitor *pad_monitor = _GET_PAD_MONITOR (pad);
   gboolean ret;
 
   GST_VALIDATE_MONITOR_LOCK (pad_monitor);
@@ -2368,8 +2442,7 @@ static gboolean
 gst_validate_pad_monitor_query_func (GstPad * pad, GstObject * parent,
     GstQuery * query)
 {
-  GstValidatePadMonitor *pad_monitor =
-      g_object_get_data ((GObject *) pad, "validate-monitor");
+  GstValidatePadMonitor *pad_monitor = _GET_PAD_MONITOR (pad);
   gboolean ret;
 
   gst_validate_pad_monitor_query_overrides (pad_monitor, query);
@@ -2430,8 +2503,7 @@ static gboolean
 gst_validate_pad_monitor_activatemode_func (GstPad * pad, GstObject * parent,
     GstPadMode mode, gboolean active)
 {
-  GstValidatePadMonitor *pad_monitor =
-      g_object_get_data ((GObject *) pad, "validate-monitor");
+  GstValidatePadMonitor *pad_monitor = _GET_PAD_MONITOR (pad);
   gboolean ret = TRUE;
 
   /* TODO add overrides for activate func */
@@ -2449,14 +2521,97 @@ gst_validate_pad_monitor_activatemode_func (GstPad * pad, GstObject * parent,
 }
 
 static GstFlowReturn
-gst_validate_pad_get_range_func (GstPad * pad, GstObject * parent,
-    guint64 offset, guint size, GstBuffer ** buffer)
+gst_validate_pad_monitor_get_range_func (GstPad * pad, GstObject * parent,
+    guint64 offset, guint length, GstBuffer ** buffer)
+{
+  GstValidatePadMonitor *pad_monitor = _GET_PAD_MONITOR (pad);
+
+  if (pad_monitor->get_range_func) {
+    GstPad *peer = gst_pad_get_peer (pad);
+    GstTask *task = NULL;
+    GThread *thread = NULL;
+
+    if (peer) {
+      GST_OBJECT_LOCK (peer);
+      task = GST_PAD_TASK (peer);
+      if (task && GST_TASK_STATE (task) == GST_TASK_STARTED) {
+        GST_OBJECT_LOCK (task);
+        /* Only doing pointer comparison, no need to hold a ref */
+        thread = task->thread;
+        GST_OBJECT_UNLOCK (task);
+      }
+      GST_OBJECT_UNLOCK (peer);
+
+      if (thread && thread != g_thread_self ()) {
+        GST_VALIDATE_REPORT (pad_monitor, PULL_RANGE_FROM_WRONG_THREAD,
+            "Pulling from wrong thread, expected pad thread: %p, got %p",
+            task->thread, g_thread_self ());
+      }
+
+      gst_object_unref (peer);
+    }
+
+    return pad_monitor->get_range_func (pad, parent, offset, length, buffer);
+  }
+
+  return GST_FLOW_NOT_SUPPORTED;
+
+}
+
+/* The interval between two buffer frequency checks */
+#define BUF_FREQ_CHECK_INTERVAL (GST_SECOND)
+
+static void
+gst_validate_pad_monitor_check_buffer_freq (GstValidatePadMonitor * monitor,
+    GstPad * pad)
 {
-  GstValidatePadMonitor *pad_monitor =
-      g_object_get_data ((GObject *) pad, "validate-monitor");
-  GstFlowReturn ret;
-  ret = pad_monitor->getrange_func (pad, parent, offset, size, buffer);
-  return ret;
+  GstClockTime ts;
+
+  if (!GST_PAD_IS_SRC (pad))
+    return;
+
+  if (!monitor->min_buf_freq)
+    return;
+
+  ts = gst_util_get_timestamp ();
+  monitor->buffers_pushed++;
+
+  /* Same logic as in fpsdisplaysink to compute the buffer frequency */
+  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID
+          (monitor->min_buf_freq_first_buffer_ts))) {
+    monitor->min_buf_freq_first_buffer_ts = ts;
+    monitor->min_buf_freq_interval_ts = ts;
+    return;
+  }
+
+  if (GST_CLOCK_DIFF (monitor->min_buf_freq_interval_ts,
+          ts) > BUF_FREQ_CHECK_INTERVAL) {
+    guint time_diff;
+    gdouble fps;
+
+    time_diff = (gdouble) (ts - monitor->min_buf_freq_interval_ts) / GST_SECOND;
+    fps =
+        (gdouble) (monitor->buffers_pushed -
+        monitor->last_buffers_pushed) / time_diff;
+
+    if (fps < monitor->min_buf_freq) {
+      if (GST_CLOCK_TIME_IS_VALID (monitor->min_buf_freq_start) &&
+          GST_CLOCK_DIFF (monitor->min_buf_freq_first_buffer_ts,
+              ts) < monitor->min_buf_freq_start) {
+        GST_DEBUG_OBJECT (pad,
+            "buffer frequency is too low (%.2f) but ignore for now (buffer-frequency-start =%"
+            GST_TIME_FORMAT ")", fps,
+            GST_TIME_ARGS (monitor->min_buf_freq_start));
+      } else {
+        GST_VALIDATE_REPORT (monitor, CONFIG_BUFFER_FREQUENCY_TOO_LOW,
+            "Buffers are not pushed fast enough on this pad: %.2f/sec (minimum: %.2f)",
+            fps, monitor->min_buf_freq);
+      }
+    }
+
+    monitor->last_buffers_pushed = monitor->buffers_pushed;
+    monitor->min_buf_freq_interval_ts = ts;
+  }
 }
 
 static gboolean
@@ -2515,6 +2670,8 @@ gst_validate_pad_monitor_buffer_probe (GstPad * pad, GstBuffer * buffer,
     }
   }
 
+  gst_validate_pad_monitor_check_buffer_freq (monitor, pad);
+
   GST_VALIDATE_MONITOR_UNLOCK (monitor);
   GST_VALIDATE_PAD_MONITOR_PARENT_UNLOCK (monitor);
   gst_validate_pad_monitor_buffer_probe_overrides (monitor, buffer);
@@ -2526,11 +2683,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;
@@ -2767,6 +2926,55 @@ gst_validate_pad_monitor_setcaps_post (GstValidatePadMonitor * pad_monitor,
   }
 }
 
+static void
+gst_validate_pad_monitor_get_min_buffer_frequency (GstValidatePadMonitor *
+    monitor, GstPad * pad)
+{
+  GList *config, *l;
+
+  if (!GST_PAD_IS_SRC (pad))
+    return;
+
+  config = gst_validate_plugin_get_config (NULL);
+  for (l = config; l != NULL; l = g_list_next (l)) {
+    GstStructure *s = l->data;
+    gdouble min_buf_freq;
+    const gchar *pad_name;
+    GstElement *element = NULL;
+
+    if (!gst_structure_get_double (s, "min-buffer-frequency", &min_buf_freq)) {
+      gint max_int;
+
+      if (!gst_structure_get_int (s, "min-buffer-frequency", &max_int))
+        goto next;
+
+      min_buf_freq = max_int;
+    }
+
+    pad_name = gst_structure_get_string (s, "name");
+    if (!pad_name)
+      pad_name = "src";
+
+    if (g_strcmp0 (GST_PAD_NAME (pad), pad_name))
+      goto next;
+
+    element = gst_pad_get_parent_element (pad);
+
+    if (!gst_validate_element_matches_target (element, s))
+      goto next;
+
+    monitor->min_buf_freq = min_buf_freq;
+
+    gst_validate_utils_get_clocktime (s, "buffer-frequency-start",
+        &monitor->min_buf_freq_start);
+
+    GST_DEBUG_OBJECT (pad, "pad has a minimum buffer frequency of %f",
+        min_buf_freq);
+  next:
+    g_clear_object (&element);
+  }
+}
+
 static gboolean
 gst_validate_pad_monitor_do_setup (GstValidateMonitor * monitor)
 {
@@ -2780,19 +2988,20 @@ gst_validate_pad_monitor_do_setup (GstValidateMonitor * monitor)
     return FALSE;
   }
 
-  if (g_object_get_data ((GObject *) pad, "validate-monitor")) {
+  if (_GET_PAD_MONITOR (pad)) {
     GST_WARNING_OBJECT (pad_monitor,
         "Pad already has a validate-monitor associated");
     gst_object_unref (pad);
     return FALSE;
   }
 
-  g_object_set_data ((GObject *) pad, "validate-monitor", pad_monitor);
+  _SET_PAD_MONITOR (pad, pad_monitor);
 
   pad_monitor->event_func = GST_PAD_EVENTFUNC (pad);
   pad_monitor->event_full_func = GST_PAD_EVENTFULLFUNC (pad);
   pad_monitor->query_func = GST_PAD_QUERYFUNC (pad);
   pad_monitor->activatemode_func = GST_PAD_ACTIVATEMODEFUNC (pad);
+  pad_monitor->get_range_func = GST_PAD_GETRANGEFUNC (pad);
   if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
 
     pad_monitor->chain_func = GST_PAD_CHAINFUNC (pad);
@@ -2806,10 +3015,6 @@ gst_validate_pad_monitor_do_setup (GstValidateMonitor * monitor)
       gst_pad_set_event_function (pad,
           gst_validate_pad_monitor_sink_event_func);
   } else {
-    pad_monitor->getrange_func = GST_PAD_GETRANGEFUNC (pad);
-    if (pad_monitor->getrange_func)
-      gst_pad_set_getrange_function (pad, gst_validate_pad_get_range_func);
-
     gst_pad_set_event_function (pad, gst_validate_pad_monitor_src_event_func);
 
     /* add buffer/event probes */
@@ -2824,12 +3029,19 @@ gst_validate_pad_monitor_do_setup (GstValidateMonitor * monitor)
   gst_pad_set_activatemode_function (pad,
       gst_validate_pad_monitor_activatemode_func);
 
+  if (GST_PAD_IS_SRC (pad)) {
+    gst_pad_set_getrange_function (pad,
+        gst_validate_pad_monitor_get_range_func);
+  }
+
   gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (monitor),
       g_strdup_printf ("%s:%s", GST_DEBUG_PAD_NAME (pad)));
 
   if (G_UNLIKELY (GST_PAD_PARENT (pad) == NULL))
     GST_FIXME ("Saw a pad not belonging to any object");
 
+  gst_validate_pad_monitor_get_min_buffer_frequency (pad_monitor, pad);
+
   gst_object_unref (pad);
   return TRUE;
 }