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