#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>
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
} \
} 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;
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;
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
}
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);
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);
}
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;
}
{
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 */
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
GST_DEBUG_OBJECT (pad, "Checking pad %s:%s input timestamps",
GST_DEBUG_PAD_NAME (otherpad));
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)
gboolean done;
GstPad *otherpad;
GstPad *peerpad;
- GstValidatePadMonitor *othermonitor;
+ GstState state, pending;
GstFlowReturn aggregated = GST_FLOW_NOT_LINKED;
gboolean found_a_pad = FALSE;
GstPad *pad =
otherpad = g_value_get_object (&value);
peerpad = gst_pad_get_peer (otherpad);
if (peerpad) {
- othermonitor = _GET_PAD_MONITOR (peerpad);
- 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);
}
/* 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) {
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 = _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);
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");
+ }
}
}
"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;
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 */
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));
}
}
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;
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:{
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;
}
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);
}
return ret;
}
+static GstFlowReturn
+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)
+{
+ 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
gst_validate_pad_monitor_buffer_probe (GstPad * pad, GstBuffer * buffer,
gpointer udata, gboolean pull_mode)
}
}
+ 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);
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;
}
}
+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)
{
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);
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;
}