From: Tim-Philipp Müller Date: Sat, 5 Mar 2016 14:12:36 +0000 (+0000) Subject: element: add API to get property change notifications via messages X-Git-Tag: 1.10.4~310 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6e3fb7af523f11ecaff63702d24723cb44e5d625;p=platform%2Fupstream%2Fgstreamer.git element: add API to get property change notifications via messages Be notified in the application thread via bus messages about notify::* and deep-notify::* property changes, instead of having to deal with it in a non-application thread. API: gst_element_add_property_notify_watch() API: gst_element_add_property_deep_notify_watch() API: gst_element_remove_property_notify_watch() API: gst_message_new_property_notify() API: gst_message_parse_property_notify() API: GST_MESSAGE_PROPERTY_NOTIFY https://bugzilla.gnome.org/show_bug.cgi?id=763142 --- diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index d5451e1..24d6aca 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -912,6 +912,11 @@ gst_element_send_event gst_element_seek_simple gst_element_seek + +gst_element_add_property_notify_watch +gst_element_add_property_deep_notify_watch +gst_element_remove_property_notify_watch + GST_ELEMENT GST_IS_ELEMENT @@ -1637,6 +1642,9 @@ gst_message_new_device_added gst_message_new_device_removed gst_message_parse_device_added gst_message_parse_device_removed + +gst_message_new_property_notify +gst_message_parse_property_notify GstMessageClass GST_MESSAGE diff --git a/gst/gstelement.c b/gst/gstelement.c index c97ad1b..75ca7b2 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -3245,3 +3245,122 @@ gst_element_get_context (GstElement * element, const gchar * context_type) return ret; } + +static void +gst_element_property_post_notify_msg (GstElement * element, GObject * obj, + GParamSpec * pspec, gboolean include_value) +{ + GValue val = G_VALUE_INIT; + GValue *v; + + GST_LOG_OBJECT (element, "property '%s' of object %" GST_PTR_FORMAT " has " + "changed, posting message with%s value", pspec->name, obj, + include_value ? "" : "out"); + + if (include_value && (pspec->flags & G_PARAM_READABLE) != 0) { + g_value_init (&val, pspec->value_type); + g_object_get_property (obj, pspec->name, &val); + v = &val; + } else { + v = NULL; + } + gst_element_post_message (element, + gst_message_new_property_notify (GST_OBJECT_CAST (obj), pspec->name, v)); +} + +static void +gst_element_property_deep_notify_cb (GstElement * element, GObject * prop_obj, + GParamSpec * pspec, gpointer user_data) +{ + gboolean include_value = GPOINTER_TO_INT (user_data); + + gst_element_property_post_notify_msg (element, prop_obj, pspec, + include_value); +} + +static void +gst_element_property_notify_cb (GObject * obj, GParamSpec * pspec, + gpointer user_data) +{ + gboolean include_value = GPOINTER_TO_INT (user_data); + + gst_element_property_post_notify_msg (GST_ELEMENT_CAST (obj), obj, pspec, + include_value); +} + +/** + * gst_element_add_property_notify_watch: + * @element: a #GstElement to watch for property changes + * @property_name: (allow-none): name of property to watch for changes, or + * NULL to watch all properties + * @include_value: whether to include the new property value in the message + * + * Returns: a watch id, which can be used in connection with + * gst_element_remove_property_notify_watch() to remove the watch again. + * + * Since: 1.10 + */ +gulong +gst_element_add_property_notify_watch (GstElement * element, + const gchar * property_name, gboolean include_value) +{ + const gchar *sep; + gchar *signal_name; + gulong id; + + g_return_val_if_fail (GST_IS_ELEMENT (element), 0); + + sep = (property_name != NULL) ? "::" : NULL; + signal_name = g_strconcat ("notify", sep, property_name, NULL); + id = g_signal_connect (element, signal_name, + G_CALLBACK (gst_element_property_notify_cb), + GINT_TO_POINTER (include_value)); + g_free (signal_name); + + return id; +} + +/** + * gst_element_add_property_deep_notify_watch: + * @element: a #GstElement to watch (recursively) for property changes + * @property_name: (allow-none): name of property to watch for changes, or + * NULL to watch all properties + * @include_value: whether to include the new property value in the message + * + * Returns: a watch id, which can be used in connection with + * gst_element_remove_property_notify_watch() to remove the watch again. + * + * Since: 1.10 + */ +gulong +gst_element_add_property_deep_notify_watch (GstElement * element, + const gchar * property_name, gboolean include_value) +{ + const gchar *sep; + gchar *signal_name; + gulong id; + + g_return_val_if_fail (GST_IS_ELEMENT (element), 0); + + sep = (property_name != NULL) ? "::" : NULL; + signal_name = g_strconcat ("deep-notify", sep, property_name, NULL); + id = g_signal_connect (element, signal_name, + G_CALLBACK (gst_element_property_deep_notify_cb), + GINT_TO_POINTER (include_value)); + g_free (signal_name); + + return id; +} + +/** + * gst_element_remove_property_notify_watch: + * @element: a #GstElement being watched for property changes + * @watch_id: watch id to remove + * + * Since: 1.10 + */ +void +gst_element_remove_property_notify_watch (GstElement * element, gulong watch_id) +{ + g_signal_handler_disconnect (element, watch_id); +} diff --git a/gst/gstelement.h b/gst/gstelement.h index f4bed31..39aa8cf 100644 --- a/gst/gstelement.h +++ b/gst/gstelement.h @@ -819,6 +819,18 @@ void gst_element_lost_state (GstElement * element); /* factory management */ GstElementFactory* gst_element_get_factory (GstElement *element); +/* utility functions */ +gulong gst_element_add_property_notify_watch (GstElement * element, + const gchar * property_name, + gboolean include_value); + +gulong gst_element_add_property_deep_notify_watch (GstElement * element, + const gchar * property_name, + gboolean include_value); + +void gst_element_remove_property_notify_watch (GstElement * element, + gulong watch_id); + #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstElement, gst_object_unref) #endif diff --git a/gst/gstmessage.c b/gst/gstmessage.c index 299ecfb..673df03 100644 --- a/gst/gstmessage.c +++ b/gst/gstmessage.c @@ -105,6 +105,7 @@ static GstMessageQuarks message_quarks[] = { {GST_MESSAGE_HAVE_CONTEXT, "have-context", 0}, {GST_MESSAGE_DEVICE_ADDED, "device-added", 0}, {GST_MESSAGE_DEVICE_REMOVED, "device-removed", 0}, + {GST_MESSAGE_PROPERTY_NOTIFY, "property-notify", 0}, {0, NULL, 0} }; @@ -2450,3 +2451,74 @@ gst_message_parse_device_removed (GstMessage * message, GstDevice ** device) gst_structure_id_get (GST_MESSAGE_STRUCTURE (message), GST_QUARK (DEVICE), GST_TYPE_DEVICE, device, NULL); } + +/** + * gst_message_new_property_notify: + * @src: The #GstObject whose property changed (may or may not be a #GstElement) + * @property_name: name of the property that changed + * @val: (allow-none) (transfer full): new property value, or %NULL + * + * Returns: a newly allocated #GstMessage + * + * Since: 1.10 + */ +GstMessage * +gst_message_new_property_notify (GstObject * src, const gchar * property_name, + GValue * val) +{ + GstStructure *structure; + GValue name_val = G_VALUE_INIT; + + g_return_val_if_fail (property_name != NULL, NULL); + + structure = gst_structure_new_id_empty (GST_QUARK (MESSAGE_PROPERTY_NOTIFY)); + g_value_init (&name_val, G_TYPE_STRING); + /* should already be interned, but let's make sure */ + g_value_set_static_string (&name_val, g_intern_string (property_name)); + gst_structure_id_take_value (structure, GST_QUARK (PROPERTY_NAME), &name_val); + if (val != NULL) + gst_structure_id_take_value (structure, GST_QUARK (PROPERTY_VALUE), val); + + return gst_message_new_custom (GST_MESSAGE_PROPERTY_NOTIFY, src, structure); +} + +/** + * gst_message_parse_property_notify: + * @message: a #GstMessage of type %GST_MESSAGE_PROPERTY_NOTIFY + * @object: (out) (allow-none) (transfer none): location where to store a + * pointer to the object whose property got changed, or %NULL + * @property_name: (out) (allow-none): return location for the name of the + * property that got changed, or %NULL + * @property_value: (out) (allow-none): return location for the new value of + * the property that got changed, or %NULL. This will only be set if the + * property notify watch was told to include the value when it was set up + * + * Parses a property-notify message. These will be posted on the bus only + * when set up with gst_element_add_property_notify_watch() or + * gst_element_add_property_deep_notify_watch(). + * + * Since: 1.10 + */ +void +gst_message_parse_property_notify (GstMessage * message, GstObject ** object, + const gchar ** property_name, const GValue ** property_value) +{ + const GstStructure *s = GST_MESSAGE_STRUCTURE (message); + + g_return_if_fail (GST_IS_MESSAGE (message)); + g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_PROPERTY_NOTIFY); + + if (object) + *object = GST_MESSAGE_SRC (message); + + if (property_name) { + const GValue *name_value; + + name_value = gst_structure_id_get_value (s, GST_QUARK (PROPERTY_NAME)); + *property_name = g_value_get_string (name_value); + } + + if (property_value) + *property_value = + gst_structure_id_get_value (s, GST_QUARK (PROPERTY_VALUE)); +} diff --git a/gst/gstmessage.h b/gst/gstmessage.h index 1ea4085..68449d0 100644 --- a/gst/gstmessage.h +++ b/gst/gstmessage.h @@ -108,6 +108,8 @@ typedef struct _GstMessage GstMessage; * a #GstDeviceProvider (Since 1.4) * @GST_MESSAGE_DEVICE_REMOVED: Message indicating a #GstDevice was removed * from a #GstDeviceProvider (Since 1.4) + * @GST_MESSAGE_PROPERTY_NOTIFY: Message indicating a #GObject property has + * changed (Since 1.10) * @GST_MESSAGE_ANY: mask for all of the above messages. * * The different message types that are available. @@ -156,6 +158,7 @@ typedef enum GST_MESSAGE_EXTENDED = (1 << 31), GST_MESSAGE_DEVICE_ADDED = GST_MESSAGE_EXTENDED + 1, GST_MESSAGE_DEVICE_REMOVED = GST_MESSAGE_EXTENDED + 2, + GST_MESSAGE_PROPERTY_NOTIFY = GST_MESSAGE_EXTENDED + 3, GST_MESSAGE_ANY = (gint) (0xffffffff) } GstMessageType; @@ -592,6 +595,9 @@ void gst_message_parse_device_added (GstMessage * message, GstDevice GstMessage * gst_message_new_device_removed (GstObject * src, GstDevice * device) G_GNUC_MALLOC; void gst_message_parse_device_removed (GstMessage * message, GstDevice ** device); +/* PROPERTY_NOTIFY */ +GstMessage * gst_message_new_property_notify (GstObject * src, const gchar * property_name, GValue * val) G_GNUC_MALLOC; +void gst_message_parse_property_notify (GstMessage * message, GstObject ** object, const gchar ** property_name, const GValue ** property_value); #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstMessage, gst_message_unref) diff --git a/gst/gstquark.c b/gst/gstquark.c index cf5c766..bfa7933 100644 --- a/gst/gstquark.c +++ b/gst/gstquark.c @@ -70,7 +70,8 @@ static const gchar *_quark_strings[] = { "GstMessageNeedContext", "GstMessageHaveContext", "context", "context-type", "GstMessageStreamStart", "group-id", "uri-redirection", "GstMessageDeviceAdded", "GstMessageDeviceRemoved", "device", - "uri-redirection-permanent" + "uri-redirection-permanent", "GstMessagePropertyNotify", "property-name", + "property-value" }; GQuark _priv_gst_quark_table[GST_QUARK_MAX]; diff --git a/gst/gstquark.h b/gst/gstquark.h index b8daeb0..5365a7e 100644 --- a/gst/gstquark.h +++ b/gst/gstquark.h @@ -202,7 +202,10 @@ typedef enum _GstQuarkId GST_QUARK_MESSAGE_DEVICE_REMOVED = 171, GST_QUARK_DEVICE = 172, GST_QUARK_URI_REDIRECTION_PERMANENT = 173, - GST_QUARK_MAX = 174 + GST_QUARK_MESSAGE_PROPERTY_NOTIFY = 174, + GST_QUARK_PROPERTY_NAME = 175, + GST_QUARK_PROPERTY_VALUE = 176, + GST_QUARK_MAX = 177 } GstQuarkId; extern GQuark _priv_gst_quark_table[GST_QUARK_MAX]; diff --git a/tests/check/gst/gstelement.c b/tests/check/gst/gstelement.c index 8732611..e2505bb 100644 --- a/tests/check/gst/gstelement.c +++ b/tests/check/gst/gstelement.c @@ -347,6 +347,141 @@ GST_START_TEST (test_pad_templates) GST_END_TEST; +/* need to return the message here because object, property name and value + * are only valid as long as we keep the message alive */ +static GstMessage * +bus_wait_for_notify_message (GstBus * bus, GstElement ** obj, + const gchar ** prop_name, const GValue ** val) +{ + GstMessage *msg; + + do { + msg = gst_bus_timed_pop_filtered (bus, -1, GST_MESSAGE_ANY); + if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_PROPERTY_NOTIFY) + break; + gst_message_unref (msg); + } while (TRUE); + + gst_message_parse_property_notify (msg, (GstObject **) obj, prop_name, val); + return msg; +} + +GST_START_TEST (test_property_notify_message) +{ + GstElement *pipeline, *identity; + gulong watch_id0, watch_id1, watch_id2, deep_watch_id1, deep_watch_id2; + GstBus *bus; + + pipeline = gst_pipeline_new (NULL); + identity = gst_element_factory_make ("identity", NULL); + gst_bin_add (GST_BIN (pipeline), identity); + + bus = GST_ELEMENT_BUS (pipeline); + + /* need to set state to READY, otherwise bus will be flushing and discard + * our messages */ + gst_element_set_state (pipeline, GST_STATE_READY); + + watch_id0 = gst_element_add_property_notify_watch (identity, NULL, FALSE); + + watch_id1 = gst_element_add_property_notify_watch (identity, "sync", FALSE); + + watch_id2 = gst_element_add_property_notify_watch (identity, "silent", TRUE); + + deep_watch_id1 = + gst_element_add_property_deep_notify_watch (pipeline, NULL, TRUE); + + deep_watch_id2 = + gst_element_add_property_deep_notify_watch (pipeline, "silent", FALSE); + + /* Now test property changes and if we get the messages we expect. We rely + * on the signals being fired in the order that they were set up here. */ + { + const GValue *val; + const gchar *name; + GstMessage *msg; + GstElement *obj; + + /* A - This should be picked up by... */ + g_object_set (identity, "dump", TRUE, NULL); + /* 1) the catch-all notify on the element (no value) */ + msg = bus_wait_for_notify_message (bus, &obj, &name, &val); + fail_unless (obj == identity); + fail_unless_equals_string (name, "dump"); + fail_unless (val == NULL); + gst_message_unref (msg); + /* 2) the catch-all deep-notify on the pipeline (with value) */ + msg = bus_wait_for_notify_message (bus, &obj, &name, &val); + fail_unless_equals_string (name, "dump"); + fail_unless (obj == identity); + fail_unless (G_VALUE_HOLDS_BOOLEAN (val)); + fail_unless_equals_int (g_value_get_boolean (val), TRUE); + gst_message_unref (msg); + + /* B - This should be picked up by... */ + g_object_set (identity, "sync", TRUE, NULL); + /* 1) the catch-all notify on the element (no value) */ + msg = bus_wait_for_notify_message (bus, &obj, &name, &val); + fail_unless (obj == identity); + fail_unless_equals_string (name, "sync"); + fail_unless (val == NULL); + gst_message_unref (msg); + /* 2) the "sync" notify on the element (no value) */ + msg = bus_wait_for_notify_message (bus, &obj, &name, &val); + fail_unless (obj == identity); + fail_unless_equals_string (name, "sync"); + fail_unless (val == NULL); + gst_message_unref (msg); + /* 3) the catch-all deep-notify on the pipeline (with value) */ + msg = bus_wait_for_notify_message (bus, &obj, &name, &val); + fail_unless_equals_string (name, "sync"); + fail_unless (obj == identity); + fail_unless (G_VALUE_HOLDS_BOOLEAN (val)); + fail_unless_equals_int (g_value_get_boolean (val), TRUE); + gst_message_unref (msg); + + /* C - This should be picked up by... */ + g_object_set (identity, "silent", FALSE, NULL); + /* 1) the catch-all notify on the element (no value) */ + msg = bus_wait_for_notify_message (bus, &obj, &name, &val); + fail_unless (obj == identity); + fail_unless_equals_string (name, "silent"); + fail_unless (val == NULL); + gst_message_unref (msg); + /* 2) the "silent" notify on the element (with value) */ + msg = bus_wait_for_notify_message (bus, &obj, &name, &val); + fail_unless (obj == identity); + fail_unless_equals_string (name, "silent"); + fail_unless (val != NULL); + fail_unless (G_VALUE_HOLDS_BOOLEAN (val)); + fail_unless_equals_int (g_value_get_boolean (val), FALSE); + gst_message_unref (msg); + /* 3) the catch-all deep-notify on the pipeline (with value) */ + msg = bus_wait_for_notify_message (bus, &obj, &name, &val); + fail_unless_equals_string (name, "silent"); + fail_unless (obj == identity); + fail_unless (G_VALUE_HOLDS_BOOLEAN (val)); + fail_unless_equals_int (g_value_get_boolean (val), FALSE); + gst_message_unref (msg); + /* 4) the "silent" deep-notify on the pipeline (without value) */ + msg = bus_wait_for_notify_message (bus, &obj, &name, &val); + fail_unless_equals_string (name, "silent"); + fail_unless (obj == identity); + fail_unless (val == NULL); + gst_message_unref (msg); + } + + gst_element_remove_property_notify_watch (identity, watch_id0); + gst_element_remove_property_notify_watch (identity, watch_id1); + gst_element_remove_property_notify_watch (identity, watch_id2); + gst_element_remove_property_notify_watch (pipeline, deep_watch_id1); + gst_element_remove_property_notify_watch (pipeline, deep_watch_id2); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; + static Suite * gst_element_suite (void) { @@ -360,6 +495,7 @@ gst_element_suite (void) tcase_add_test (tc_chain, test_link); tcase_add_test (tc_chain, test_link_no_pads); tcase_add_test (tc_chain, test_pad_templates); + tcase_add_test (tc_chain, test_property_notify_message); return s; } diff --git a/win32/common/libgstreamer.def b/win32/common/libgstreamer.def index c41c86a..0b54e8d 100644 --- a/win32/common/libgstreamer.def +++ b/win32/common/libgstreamer.def @@ -476,6 +476,8 @@ EXPORTS gst_double_range_get_type gst_element_abort_state gst_element_add_pad + gst_element_add_property_deep_notify_watch + gst_element_add_property_notify_watch gst_element_change_state gst_element_class_add_metadata gst_element_class_add_pad_template @@ -545,6 +547,7 @@ EXPORTS gst_element_register gst_element_release_request_pad gst_element_remove_pad + gst_element_remove_property_notify_watch gst_element_request_pad gst_element_seek gst_element_seek_simple @@ -712,6 +715,7 @@ EXPORTS gst_message_new_need_context gst_message_new_new_clock gst_message_new_progress + gst_message_new_property_notify gst_message_new_qos gst_message_new_request_state gst_message_new_reset_time @@ -741,6 +745,7 @@ EXPORTS gst_message_parse_info gst_message_parse_new_clock gst_message_parse_progress + gst_message_parse_property_notify gst_message_parse_qos gst_message_parse_qos_stats gst_message_parse_qos_values