validate:scenario: Add support for waiting on signals and messages
authorRamiro Polla <ramiro.polla@collabora.co.uk>
Fri, 13 Feb 2015 17:34:04 +0000 (18:34 +0100)
committerThibault Saunier <tsaunier@gnome.org>
Mon, 13 Apr 2015 09:27:02 +0000 (11:27 +0200)
Reviewers: Mathieu_Du

Differential Revision:
http://phabricator.freedesktop.org/D88

validate/gst/validate/gst-validate-scenario.c

index 7ea3e95..ac9b753 100644 (file)
@@ -125,9 +125,14 @@ struct _GstValidateScenarioPrivate
 
   guint get_pos_id;             /* MT safe. Protect with SCENARIO_LOCK */
   guint wait_id;
+  guint signal_handler_id;
+
+  /* Name of message the wait action is waiting for */
+  const gchar *message_type;
 
   gboolean buffering;
 
+  gboolean got_eos;
   gboolean changing_state;
   GstState target_state;
 
@@ -907,7 +912,8 @@ _add_execute_actions_gsource (GstValidateScenario * scenario)
   GstValidateScenarioPrivate *priv = scenario->priv;
 
   SCENARIO_LOCK (scenario);
-  if (priv->get_pos_id == 0 && priv->wait_id == 0) {
+  if (priv->get_pos_id == 0 && priv->wait_id == 0
+      && priv->signal_handler_id == 0 && priv->message_type == NULL) {
     priv->get_pos_id = g_idle_add ((GSourceFunc) execute_next_action, scenario);
     SCENARIO_UNLOCK (scenario);
 
@@ -916,7 +922,7 @@ _add_execute_actions_gsource (GstValidateScenario * scenario)
   }
   SCENARIO_UNLOCK (scenario);
 
-  GST_DEBUG_OBJECT (scenario, "No need to start a new gsource");
+  GST_LOG_OBJECT (scenario, "No need to start a new gsource");
   return FALSE;
 }
 
@@ -969,6 +975,9 @@ _should_execute_action (GstValidateScenario * scenario, GstValidateAction * act,
     GST_DEBUG_OBJECT (scenario, "No action to execute");
 
     return FALSE;
+  } else if (scenario->priv->got_eos) {
+    GST_DEBUG_OBJECT (scenario, "Just got EOS go and execute next action!");
+    scenario->priv->got_eos = FALSE;
   } else if (GST_STATE (scenario->pipeline) < GST_STATE_PAUSED) {
     GST_DEBUG_OBJECT (scenario, "Pipeline not even in paused, "
         "just executing actions");
@@ -1275,7 +1284,7 @@ execute_next_action (GstValidateScenario * scenario)
     return G_SOURCE_CONTINUE;
   }
 
-  if (has_pos && has_dur) {
+  if (has_pos && has_dur && !priv->got_eos) {
     if (position > duration) {
       _add_execute_actions_gsource (scenario);
 
@@ -1398,8 +1407,27 @@ stop_waiting (GstValidateAction * action)
   return G_SOURCE_REMOVE;
 }
 
+static GstElement *_get_target_element (GstValidateScenario * scenario,
+    GstValidateAction * action);
+
+static void
+stop_waiting_signal (GstBin * bin, GstElement * element,
+    GstValidateAction * action)
+{
+  GstValidateScenario *scenario = action->scenario;
+  GstValidateScenarioPrivate *priv = scenario->priv;
+
+  gst_validate_printf (scenario, "Stop waiting for signal\n");
+
+  g_signal_handler_disconnect (bin, priv->signal_handler_id);
+
+  priv->signal_handler_id = 0;
+  gst_validate_action_set_done (action);
+  _add_execute_actions_gsource (scenario);
+}
+
 static gboolean
-_execute_wait (GstValidateScenario * scenario, GstValidateAction * action)
+_execute_timed_wait (GstValidateScenario * scenario, GstValidateAction * action)
 {
   GstValidateScenarioPrivate *priv = scenario->priv;
   GstClockTime duration;
@@ -1449,6 +1477,75 @@ _execute_wait (GstValidateScenario * scenario, GstValidateAction * action)
 }
 
 static gboolean
+_execute_wait_for_signal (GstValidateScenario * scenario,
+    GstValidateAction * action)
+{
+  GstValidateScenarioPrivate *priv = scenario->priv;
+  const gchar *signal_name = gst_structure_get_string
+      (action->structure, "signal-name");
+  GstElement *target;
+
+  if (signal_name == NULL) {
+    GST_ERROR ("No signal-name given for wait action");
+    return FALSE;
+  }
+
+  target = _get_target_element (scenario, action);
+  if (target == NULL) {
+    return FALSE;
+  }
+
+  gst_validate_printf (action, "Waiting for '%s' signal\n", signal_name);
+
+  if (priv->get_pos_id) {
+    g_source_remove (priv->get_pos_id);
+    priv->get_pos_id = 0;
+  }
+
+  priv->signal_handler_id =
+      g_signal_connect (target, signal_name, (GCallback) stop_waiting_signal,
+      action);
+
+  gst_object_unref (target);
+
+  return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
+}
+
+static gboolean
+_execute_wait_for_message (GstValidateScenario * scenario,
+    GstValidateAction * action)
+{
+  GstValidateScenarioPrivate *priv = scenario->priv;
+  const gchar *message_type = gst_structure_get_string
+      (action->structure, "message-type");
+
+  gst_validate_printf (action, "Waiting for '%s' message\n", message_type);
+
+  if (priv->get_pos_id) {
+    g_source_remove (priv->get_pos_id);
+    priv->get_pos_id = 0;
+  }
+
+  priv->message_type = g_strdup (message_type);
+
+  return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
+}
+
+static gboolean
+_execute_wait (GstValidateScenario * scenario, GstValidateAction * action)
+{
+  if (gst_structure_has_field (action->structure, "signal-name")) {
+    return _execute_wait_for_signal (scenario, action);
+  } else if (gst_structure_has_field (action->structure, "message-type")) {
+    return _execute_wait_for_message (scenario, action);
+  } else {
+    return _execute_timed_wait (scenario, action);
+  }
+
+  return FALSE;
+}
+
+static gboolean
 _execute_dot_pipeline (GstValidateScenario * scenario,
     GstValidateAction * action)
 {
@@ -1478,6 +1575,10 @@ _get_target_element (GstValidateScenario * scenario, GstValidateAction * action)
   GstElement *target;
 
   name = gst_structure_get_string (action->structure, "target-element-name");
+  if (name == NULL) {
+    return NULL;
+  }
+
   if (strcmp (GST_OBJECT_NAME (scenario->pipeline), name) == 0) {
     target = gst_object_ref (scenario->pipeline);
   } else {
@@ -1669,9 +1770,30 @@ gst_validate_action_default_prepare_func (GstValidateAction * action)
   return TRUE;
 }
 
+static void
+_check_waiting_for_message (GstValidateScenario * scenario,
+    GstMessage * message)
+{
+  GstValidateScenarioPrivate *priv = scenario->priv;
+
+  if (!g_strcmp0 (priv->message_type,
+          gst_message_type_get_name (GST_MESSAGE_TYPE (message)))) {
+    GstValidateAction *action = scenario->priv->actions->data;
+
+    g_free ((gpointer) priv->message_type);
+    priv->message_type = NULL;
+
+    gst_validate_printf (scenario, "Stop waiting for message\n");
+
+    gst_validate_action_set_done (action);
+    _add_execute_actions_gsource (scenario);
+  }
+}
+
 static gboolean
 message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
 {
+  gboolean is_error = FALSE;
   GstValidateScenarioPrivate *priv = scenario->priv;
 
   switch (GST_MESSAGE_TYPE (message)) {
@@ -1726,11 +1848,30 @@ message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
       break;
     }
     case GST_MESSAGE_ERROR:
+      is_error = TRUE;
+
+      /* Passtrough */
     case GST_MESSAGE_EOS:
     {
       GstValidateAction *stop_action;
       GstValidateActionType *stop_action_type;
 
+      if (!is_error) {
+        priv->got_eos = TRUE;
+        if (priv->message_type) {
+
+          if (priv->actions->next) {
+            GST_DEBUG_OBJECT (scenario,
+                "Waiting for a message and got a next action"
+                " to execute, letting it a chance!");
+            goto done;
+          } else {
+            /* Clear current message wait if waiting for EOS */
+            _check_waiting_for_message (scenario, message);
+          }
+        }
+      }
+
       SCENARIO_LOCK (scenario);
       if (scenario->priv->actions || scenario->priv->interlaced_actions ||
           scenario->priv->on_addition_actions) {
@@ -1749,7 +1890,8 @@ message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
 
           tmpconcat = actions;
 
-          if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL) {
+          if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL ||
+              action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) {
             gst_validate_action_unref (action);
 
             continue;
@@ -1775,6 +1917,7 @@ message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
       }
       SCENARIO_UNLOCK (scenario);
 
+
       stop_action_type = _find_action_type ("stop");
       stop_action = gst_validate_action_new (scenario, stop_action_type);
       _fill_action (scenario, stop_action,
@@ -1801,6 +1944,11 @@ message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
       break;
   }
 
+done:
+  /* Check if we got the message expected by a wait action */
+  if (priv->message_type)
+    _check_waiting_for_message (scenario, message);
+
   return TRUE;
 }
 
@@ -2918,11 +3066,33 @@ init_scenarios (void)
         {
           .name = "duration",
           .description = "the duration while no other action will be executed",
-          .mandatory = TRUE,
+          .mandatory = FALSE,
           NULL},
+        {
+          .name = "target-element-name",
+          .description = "The name of the GstElement to wait @signal-name on.",
+          .mandatory = FALSE,
+          .types = "string"
+        },
+        {
+          .name = "signal-name",
+          .description = "The name of the signal to wait for on @target-element-name",
+          .mandatory = FALSE,
+          .types = "string",
+          NULL
+        },
+        {
+          .name = "message-type",
+          .description = "The name of the message type to wait for (on @target-element-name"
+            " if specified)",
+          .mandatory = FALSE,
+          .types = "string",
+          NULL
+        },
         {NULL}
       }),
-      "Waits during 'duration' seconds", GST_VALIDATE_ACTION_TYPE_NONE);
+      "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",
+      GST_VALIDATE_ACTION_TYPE_NONE);
 
   REGISTER_ACTION_TYPE ("dot-pipeline", _execute_dot_pipeline, NULL,
       "Dots the pipeline (the 'name' property will be used in the dot filename).\n"