+2005-08-17 Andy Wingo <wingo@pobox.com>
+
+ * gst/base/gstbasetransform.c: Debug changes.
+
+ * gst/gstutils.h:
+ * gst/gstutils.c (gst_bin_watch_for_state_change): Add function to
+ ensure bins post state change messages. A bit of a hack but I can't
+ think of a way to avoid it.
+
+ * check/gst/gstbin.c (test_watch_for_state_change): Added test.
+
2005-08-16 Andy Wingo <wingo@pobox.com>
* gst/base/gstadapter.h:
GST_END_TEST;
+GST_START_TEST (test_watch_for_state_change)
+{
+ GstElement *src, *sink, *bin;
+ GstBus *bus;
+
+ bin = gst_element_factory_make ("bin", NULL);
+ fail_unless (bin != NULL, "Could not create bin");
+
+ src = gst_element_factory_make ("fakesrc", NULL);
+ fail_if (src == NULL, "Could not create fakesrc");
+ sink = gst_element_factory_make ("fakesink", NULL);
+ fail_if (sink == NULL, "Could not create fakesink");
+
+ gst_bin_add (GST_BIN (bin), sink);
+ gst_bin_add (GST_BIN (bin), src);
+
+ fail_unless (gst_element_link (src, sink), "could not link src and sink");
+
+ bus = GST_ELEMENT_BUS (bin);
+
+ /* change state, spawning two times three messages, minus one async */
+ fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED)
+ == GST_STATE_ASYNC);
+
+ pop_messages (bus, 5);
+
+ fail_unless (gst_bus_have_pending (bus) == FALSE,
+ "Unexpected messages on bus");
+
+ gst_bin_watch_for_state_change (GST_BIN (bin));
+
+ /* should get the bin's state change message now */
+ pop_messages (bus, 1);
+
+ fail_unless (gst_bus_have_pending (bus) == FALSE,
+ "Unexpected messages on bus");
+
+ fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING)
+ == GST_STATE_SUCCESS);
+
+ pop_messages (bus, 3);
+
+ /* this one might return either SUCCESS or ASYNC, likely SUCCESS */
+ gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED);
+
+ gst_bin_watch_for_state_change (GST_BIN (bin));
+
+ pop_messages (bus, 3);
+
+ fail_unless (gst_bus_have_pending (bus) == FALSE,
+ "Unexpected messages on bus");
+
+ /* setting bin to NULL flushes the bus automatically */
+ fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL)
+ == GST_STATE_SUCCESS);
+
+ /* clean up */
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
/* adding an element with linked pads to a bin unlinks the
* pads */
GST_START_TEST (test_add_linked)
tcase_add_test (tc_chain, test_message_state_changed);
tcase_add_test (tc_chain, test_message_state_changed_child);
tcase_add_test (tc_chain, test_message_state_changed_children);
+ tcase_add_test (tc_chain, test_watch_for_state_change);
tcase_add_test (tc_chain, test_add_linked);
return s;
klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
- GST_DEBUG_OBJECT (trans, "from: %" GST_PTR_FORMAT, caps);
-
/* if there is a custom transform function, use this */
if (klass->transform_caps) {
GstCaps *temp;
GstCaps *nth;
nth = gst_caps_copy_nth (caps, i);
- GST_DEBUG_OBJECT (trans, " from: %" GST_PTR_FORMAT, nth);
+ GST_DEBUG_OBJECT (trans, "from[%d]: %" GST_PTR_FORMAT, i, nth);
temp = klass->transform_caps (trans, pad, nth);
gst_caps_unref (nth);
- GST_DEBUG_OBJECT (trans, " to : %" GST_PTR_FORMAT, temp);
+ GST_DEBUG_OBJECT (trans, " to[%d]: %" GST_PTR_FORMAT, i, temp);
gst_caps_append (ret, temp);
}
if (unref)
gst_object_unref (p);
}
+
static void
remove_all_from_queue (GQueue * queue, gpointer elem, gboolean unref)
{
}
static void
+get_state_func (GstElement * element, gpointer unused)
+{
+ GstElementStateReturn ret = GST_STATE_ASYNC;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "new thread waiting on state change");
+
+ /* wait indefinitely */
+ while (ret == GST_STATE_ASYNC)
+ ret = gst_element_get_state (element, NULL, NULL, NULL);
+
+ gst_object_unref (element);
+}
+
+/**
+ * gst_bin_watch_for_state_change:
+ * @bin: the bin to watch for state changes
+ *
+ * Spawns a thread calling gst_element_get_state on @bin with infinite timeout.
+ *
+ * In practice this is done because if a bin returns GST_STATE_ASYNC from a
+ * state change it will not commit its state until someone calls
+ * gst_element_get_state on it. Thus having another thread checking the bin's
+ * state will ensure that a state-changed message gets posted on the bus
+ * eventually.
+ *
+ * This function is admittedly a bit of a hack. Bins should always post
+ * messages. However this behavior was broken out into this function to avoid
+ * spawning threads when scrubbing, when the bin's state is changing quickly and
+ * asynchronously.
+ */
+void
+gst_bin_watch_for_state_change (GstBin * bin)
+{
+ static GThreadPool *pool = NULL;
+ static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+
+ g_static_mutex_lock (&mutex);
+ if (pool == NULL)
+ pool = g_thread_pool_new ((GFunc) get_state_func, NULL, -1, FALSE, NULL);
+ g_static_mutex_unlock (&mutex);
+
+ g_thread_pool_push (pool, gst_object_ref (bin), NULL);
+}
+
+static void
gst_element_populate_std_props (GObjectClass * klass, const gchar * prop_name,
guint arg_id, GParamFlags flags)
{
/* bin functions */
void gst_bin_add_many (GstBin *bin, GstElement *element_1, ...);
void gst_bin_remove_many (GstBin *bin, GstElement *element_1, ...);
+void gst_bin_watch_for_state_change (GstBin *bin);
/* buffer functions */
GstBuffer * gst_buffer_merge (GstBuffer * buf1, GstBuffer * buf2);
klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
- GST_DEBUG_OBJECT (trans, "from: %" GST_PTR_FORMAT, caps);
-
/* if there is a custom transform function, use this */
if (klass->transform_caps) {
GstCaps *temp;
GstCaps *nth;
nth = gst_caps_copy_nth (caps, i);
- GST_DEBUG_OBJECT (trans, " from: %" GST_PTR_FORMAT, nth);
+ GST_DEBUG_OBJECT (trans, "from[%d]: %" GST_PTR_FORMAT, i, nth);
temp = klass->transform_caps (trans, pad, nth);
gst_caps_unref (nth);
- GST_DEBUG_OBJECT (trans, " to : %" GST_PTR_FORMAT, temp);
+ GST_DEBUG_OBJECT (trans, " to[%d]: %" GST_PTR_FORMAT, i, temp);
gst_caps_append (ret, temp);
}
GST_END_TEST;
+GST_START_TEST (test_watch_for_state_change)
+{
+ GstElement *src, *sink, *bin;
+ GstBus *bus;
+
+ bin = gst_element_factory_make ("bin", NULL);
+ fail_unless (bin != NULL, "Could not create bin");
+
+ src = gst_element_factory_make ("fakesrc", NULL);
+ fail_if (src == NULL, "Could not create fakesrc");
+ sink = gst_element_factory_make ("fakesink", NULL);
+ fail_if (sink == NULL, "Could not create fakesink");
+
+ gst_bin_add (GST_BIN (bin), sink);
+ gst_bin_add (GST_BIN (bin), src);
+
+ fail_unless (gst_element_link (src, sink), "could not link src and sink");
+
+ bus = GST_ELEMENT_BUS (bin);
+
+ /* change state, spawning two times three messages, minus one async */
+ fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED)
+ == GST_STATE_ASYNC);
+
+ pop_messages (bus, 5);
+
+ fail_unless (gst_bus_have_pending (bus) == FALSE,
+ "Unexpected messages on bus");
+
+ gst_bin_watch_for_state_change (GST_BIN (bin));
+
+ /* should get the bin's state change message now */
+ pop_messages (bus, 1);
+
+ fail_unless (gst_bus_have_pending (bus) == FALSE,
+ "Unexpected messages on bus");
+
+ fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING)
+ == GST_STATE_SUCCESS);
+
+ pop_messages (bus, 3);
+
+ /* this one might return either SUCCESS or ASYNC, likely SUCCESS */
+ gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED);
+
+ gst_bin_watch_for_state_change (GST_BIN (bin));
+
+ pop_messages (bus, 3);
+
+ fail_unless (gst_bus_have_pending (bus) == FALSE,
+ "Unexpected messages on bus");
+
+ /* setting bin to NULL flushes the bus automatically */
+ fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL)
+ == GST_STATE_SUCCESS);
+
+ /* clean up */
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
/* adding an element with linked pads to a bin unlinks the
* pads */
GST_START_TEST (test_add_linked)
tcase_add_test (tc_chain, test_message_state_changed);
tcase_add_test (tc_chain, test_message_state_changed_child);
tcase_add_test (tc_chain, test_message_state_changed_children);
+ tcase_add_test (tc_chain, test_watch_for_state_change);
tcase_add_test (tc_chain, test_add_linked);
return s;