clapper: Add "message" signal to the player

A detailed signal that allows for applications to receive element
messages posted on the pipeline bus. Useful if app cares about
some custom message that player normally does not handle.

For example audio player might want to read "level" messages in
order to show current audio level or implement visualizations.
This commit is contained in:
Rafał Dzięgiel
2025-08-02 20:21:52 +02:00
parent b9a8b28a1f
commit b0b15cec5c
4 changed files with 81 additions and 0 deletions

View File

@@ -52,6 +52,8 @@ void clapper_app_bus_post_object_desc_signal (ClapperAppBus *app_bus, GstObject
void clapper_app_bus_post_desc_with_details_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, const gchar *desc, const gchar *details); void clapper_app_bus_post_desc_with_details_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, const gchar *desc, const gchar *details);
void clapper_app_bus_post_message_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, GstMessage *msg);
void clapper_app_bus_post_error_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, GError *error, const gchar *debug_info); void clapper_app_bus_post_error_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, GError *error, const gchar *debug_info);
G_END_DECLS G_END_DECLS

View File

@@ -46,6 +46,7 @@ enum
CLAPPER_APP_BUS_STRUCTURE_SIMPLE_SIGNAL, CLAPPER_APP_BUS_STRUCTURE_SIMPLE_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_OBJECT_DESC_SIGNAL, CLAPPER_APP_BUS_STRUCTURE_OBJECT_DESC_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_DESC_WITH_DETAILS_SIGNAL, CLAPPER_APP_BUS_STRUCTURE_DESC_WITH_DETAILS_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_MESSAGE_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_ERROR_SIGNAL CLAPPER_APP_BUS_STRUCTURE_ERROR_SIGNAL
}; };
@@ -58,6 +59,7 @@ static ClapperBusQuark _structure_quarks[] = {
{"simple-signal", 0}, {"simple-signal", 0},
{"object-desc-signal", 0}, {"object-desc-signal", 0},
{"desc-with-details-signal", 0}, {"desc-with-details-signal", 0},
{"message", 0},
{"error-signal", 0}, {"error-signal", 0},
{NULL, 0} {NULL, 0}
}; };
@@ -276,6 +278,53 @@ _handle_desc_with_details_signal_msg (GstMessage *msg, const GstStructure *struc
g_free (details); g_free (details);
} }
void
clapper_app_bus_post_message_signal (ClapperAppBus *self,
GstObject *src, guint signal_id, GstMessage *msg)
{
/* Check for any "message" signal connection */
if (g_signal_handler_find (src, G_SIGNAL_MATCH_ID,
signal_id, 0, NULL, NULL, NULL) != 0) {
const GstStructure *structure = gst_message_get_structure (msg);
GQuark detail;
if (G_UNLIKELY (structure == NULL))
return;
detail = g_quark_from_string (gst_structure_get_name (structure));
/* If specific detail is connected or ALL "message" handler */
if (g_signal_handler_find (src, G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL,
signal_id, detail, NULL, NULL, NULL) != 0
|| g_signal_handler_find (src, G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL,
signal_id, 0, NULL, NULL, NULL) != 0) {
GstStructure *structure = gst_structure_new_id (_STRUCTURE_QUARK (MESSAGE_SIGNAL),
_FIELD_QUARK (SIGNAL_ID), G_TYPE_UINT, signal_id,
_FIELD_QUARK (DETAILS), G_TYPE_UINT, detail,
_FIELD_QUARK (OBJECT), GST_TYPE_MESSAGE, msg,
NULL);
gst_bus_post (GST_BUS_CAST (self), gst_message_new_application (src, structure));
}
}
}
static inline void
_handle_message_signal_msg (GstMessage *msg, const GstStructure *structure)
{
guint signal_id = 0;
GQuark detail = 0;
GstMessage *fwd_message = NULL;
gst_structure_id_get (structure,
_FIELD_QUARK (SIGNAL_ID), G_TYPE_UINT, &signal_id,
_FIELD_QUARK (DETAILS), G_TYPE_UINT, &detail,
_FIELD_QUARK (OBJECT), GST_TYPE_MESSAGE, &fwd_message,
NULL);
g_signal_emit (_MESSAGE_SRC_GOBJECT (msg), signal_id, detail, fwd_message);
gst_message_unref (fwd_message);
}
void void
clapper_app_bus_post_error_signal (ClapperAppBus *self, clapper_app_bus_post_error_signal (ClapperAppBus *self,
GstObject *src, guint signal_id, GstObject *src, guint signal_id,
@@ -326,6 +375,8 @@ clapper_app_bus_message_func (GstBus *bus, GstMessage *msg, gpointer user_data G
_handle_simple_signal_msg (msg, structure); _handle_simple_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (OBJECT_DESC_SIGNAL)) else if (quark == _STRUCTURE_QUARK (OBJECT_DESC_SIGNAL))
_handle_object_desc_signal_msg (msg, structure); _handle_object_desc_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (MESSAGE_SIGNAL))
_handle_message_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (ERROR_SIGNAL)) else if (quark == _STRUCTURE_QUARK (ERROR_SIGNAL))
_handle_error_signal_msg (msg, structure); _handle_error_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (DESC_WITH_DETAILS_SIGNAL)) else if (quark == _STRUCTURE_QUARK (DESC_WITH_DETAILS_SIGNAL))

View File

@@ -930,6 +930,11 @@ _handle_element_msg (GstMessage *msg, ClapperPlayer *player)
GST_OBJECT_CAST (downloaded_item), location); GST_OBJECT_CAST (downloaded_item), location);
gst_object_unref (downloaded_item); gst_object_unref (downloaded_item);
} else {
guint signal_id = g_signal_lookup ("message", CLAPPER_TYPE_PLAYER);
clapper_app_bus_post_message_signal (player->app_bus,
GST_OBJECT_CAST (player), signal_id, msg);
} }
} }

View File

@@ -111,6 +111,7 @@ enum
SIGNAL_SEEK_DONE, SIGNAL_SEEK_DONE,
SIGNAL_DOWNLOAD_COMPLETE, SIGNAL_DOWNLOAD_COMPLETE,
SIGNAL_MISSING_PLUGIN, SIGNAL_MISSING_PLUGIN,
SIGNAL_MESSAGE,
SIGNAL_WARNING, SIGNAL_WARNING,
SIGNAL_ERROR, SIGNAL_ERROR,
SIGNAL_LAST SIGNAL_LAST
@@ -2985,6 +2986,28 @@ clapper_player_class_init (ClapperPlayerClass *klass)
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
/**
* ClapperPlayer::message:
* @player: a #ClapperPlayer
* @msg: a #GstMessage
*
* Allows for applications to receive element messages posted
* on the underlaying pipeline bus.
*
* This is a detailed signal. Connect to it via `message::name`
* to only receive messages with a certain `name`.
*
* Player will only forward messages to the main app thread (from which
* this signal is emitted) that have a matching signal handler, thus
* it is more efficient to listen only for specific messages instead
* of connecting to simply `message` with no details (without message name).
*
* Since: 0.10
*/
signals[SIGNAL_MESSAGE] = g_signal_new ("message", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS | G_SIGNAL_DETAILED,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_MESSAGE);
/** /**
* ClapperPlayer::warning: * ClapperPlayer::warning:
* @player: a #ClapperPlayer * @player: a #ClapperPlayer