From 984f0c2d2f1759508886e1ba3ddb7e55acc8059a Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Wed, 25 Nov 2020 22:25:28 -0300 Subject: [PATCH] transcoder: Port to a GstBus API instead Following the move made by GstPlayer in: https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/35 Part-of: --- .../gsttranscoder-message-private.h | 30 + .../transcoder/gsttranscoder-signal-adapter.c | 342 +++++++++ .../transcoder/gsttranscoder-signal-adapter.h | 61 ++ gst-libs/gst/transcoder/gsttranscoder.c | 690 ++++++------------ gst-libs/gst/transcoder/gsttranscoder.h | 102 +-- gst-libs/gst/transcoder/meson.build | 4 +- tools/gst-transcoder.c | 18 +- 7 files changed, 735 insertions(+), 512 deletions(-) create mode 100644 gst-libs/gst/transcoder/gsttranscoder-message-private.h create mode 100644 gst-libs/gst/transcoder/gsttranscoder-signal-adapter.c create mode 100644 gst-libs/gst/transcoder/gsttranscoder-signal-adapter.h diff --git a/gst-libs/gst/transcoder/gsttranscoder-message-private.h b/gst-libs/gst/transcoder/gsttranscoder-message-private.h new file mode 100644 index 0000000000..2c2ba213d8 --- /dev/null +++ b/gst-libs/gst/transcoder/gsttranscoder-message-private.h @@ -0,0 +1,30 @@ +/* GStreamer + * + * Copyright (C) 2020 Stephan Hesse + * Copyright (C) 2020 Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#define GST_TRANSCODER_MESSAGE_DATA "gst-transcoder-message-data" +#define GST_TRANSCODER_MESSAGE_DATA_TYPE "transcoder-message-type" +#define GST_TRANSCODER_MESSAGE_DATA_POSITION "position" +#define GST_TRANSCODER_MESSAGE_DATA_DURATION "duration" +#define GST_TRANSCODER_MESSAGE_DATA_ERROR "error" +#define GST_TRANSCODER_MESSAGE_DATA_WARNING "warning" +#define GST_TRANSCODER_MESSAGE_DATA_ISSUE_DETAILS "issue-details" diff --git a/gst-libs/gst/transcoder/gsttranscoder-signal-adapter.c b/gst-libs/gst/transcoder/gsttranscoder-signal-adapter.c new file mode 100644 index 0000000000..ac41275986 --- /dev/null +++ b/gst-libs/gst/transcoder/gsttranscoder-signal-adapter.c @@ -0,0 +1,342 @@ +/* GStreamer + * + * Copyright (C) 2019-2020 Stephan Hesse + * Copyright (C) 2020 Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gsttranscoder.h" +#include "gsttranscoder-signal-adapter.h" + +#include "gsttranscoder-message-private.h" + +#include + +GST_DEBUG_CATEGORY_STATIC (gst_transcoder_signal_adapter_debug); +#define GST_CAT_DEFAULT gst_transcoder_signal_adapter_debug + +enum +{ + SIGNAL_POSITION_UPDATED, + SIGNAL_DURATION_CHANGED, + SIGNAL_DONE, + SIGNAL_ERROR, + SIGNAL_WARNING, + SIGNAL_LAST +}; + +enum +{ + PROP_0, + PROP_TRANSCODER, + PROP_LAST +}; + +static GParamSpec *param_specs[PROP_LAST] = { NULL, }; + +struct _GstTranscoderSignalAdapter +{ + GObject parent; + GstBus *bus; + GSource *source; + + GstTranscoder *transcoder; +}; + +struct _GstTranscoderSignalAdapterClass +{ + GObjectClass parent_class; +}; + +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (gst_transcoder_signal_adapter_debug, "gst-transcoder-signaladapter", \ + 0, "GstTranscoder signal adapter") + +#define parent_class gst_transcoder_signal_adapter_parent_class +G_DEFINE_TYPE_WITH_CODE (GstTranscoderSignalAdapter, + gst_transcoder_signal_adapter, G_TYPE_OBJECT, _do_init); + +static guint signals[SIGNAL_LAST] = { 0, }; + +static void +gst_transcoder_signal_adapter_emit (GstTranscoderSignalAdapter * self, + const GstStructure * message_data) +{ + GstTranscoderMessage transcoder_message_type; + g_return_if_fail (g_str_equal (gst_structure_get_name (message_data), + GST_TRANSCODER_MESSAGE_DATA)); + + GST_LOG ("Emitting message %" GST_PTR_FORMAT, message_data); + gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_TYPE, + GST_TYPE_TRANSCODER_MESSAGE, &transcoder_message_type, NULL); + + switch (transcoder_message_type) { + case GST_TRANSCODER_MESSAGE_POSITION_UPDATED:{ + GstClockTime pos = GST_CLOCK_TIME_NONE; + gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_POSITION, + GST_TYPE_CLOCK_TIME, &pos, NULL); + g_signal_emit (self, signals[SIGNAL_POSITION_UPDATED], 0, pos); + break; + } + case GST_TRANSCODER_MESSAGE_DURATION_CHANGED:{ + GstClockTime duration = GST_CLOCK_TIME_NONE; + gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_DURATION, + GST_TYPE_CLOCK_TIME, &duration, NULL); + g_signal_emit (self, signals[SIGNAL_DURATION_CHANGED], 0, duration); + break; + } + case GST_TRANSCODER_MESSAGE_DONE: + g_signal_emit (self, signals[SIGNAL_DONE], 0); + break; + case GST_TRANSCODER_MESSAGE_ERROR:{ + GError *error = NULL; + GstStructure *details; + + gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_ERROR, + G_TYPE_ERROR, &error, GST_TYPE_STRUCTURE, &details, NULL); + g_signal_emit (self, signals[SIGNAL_ERROR], 0, error, details); + g_error_free (error); + if (details) + gst_structure_free (details); + break; + } + case GST_TRANSCODER_MESSAGE_WARNING:{ + GstStructure *details; + GError *error = NULL; + + gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_WARNING, + G_TYPE_ERROR, &error, GST_TYPE_STRUCTURE, &details, NULL); + g_signal_emit (self, signals[SIGNAL_WARNING], 0, error, details); + g_error_free (error); + if (details) + gst_structure_free (details); + break; + } + default: + g_assert_not_reached (); + break; + } +} + +/* + * callback for the bus-message in-sync handling + */ +static GstBusSyncReply + gst_transcoder_signal_adapter_bus_sync_handler + (GstBus * bus, GstMessage * message, gpointer user_data) +{ + GstTranscoderSignalAdapter *self = GST_TRANSCODER_SIGNAL_ADAPTER (user_data); + const GstStructure *message_data = gst_message_get_structure (message); + gst_transcoder_signal_adapter_emit (self, message_data); + gst_message_unref (message); + return GST_BUS_DROP; +} + +/* + * callback for the bus-watch + * pre: there is a message on the bus + */ +static gboolean +gst_transcoder_signal_adapter_on_message (GstBus * bus, + GstMessage * message, gpointer user_data) +{ + GstTranscoderSignalAdapter *self = GST_TRANSCODER_SIGNAL_ADAPTER (user_data); + const GstStructure *message_data = gst_message_get_structure (message); + gst_transcoder_signal_adapter_emit (self, message_data); + return TRUE; +} + +/** + * gst_transcoder_signal_adapter_new: + * @transcoder: (transfer none): #GstTranscoder instance to emit signals for. + * @context: (nullable): A #GMainContext on which the main-loop will process + * transcoder bus messages on. Can be NULL (thread-default + * context will be used then). + * + * A bus-watching #GSource will be created and attached to the context. The + * attached callback will emit the corresponding signal for the message + * received. Matching signals for transcoder messages from the bus will be + * emitted by it on the created adapter object. + * + * Returns: (transfer full)(nullable): A new #GstTranscoderSignalAdapter to + * connect signal handlers to. + * + * Since: 1.20 + */ +GstTranscoderSignalAdapter * +gst_transcoder_signal_adapter_new (GstTranscoder * transcoder, + GMainContext * context) +{ + GstTranscoderSignalAdapter *self = NULL; + + g_return_val_if_fail (GST_IS_TRANSCODER (transcoder), NULL); + + if (!context) { + context = g_main_context_get_thread_default (); + } + + self = g_object_new (GST_TYPE_TRANSCODER_SIGNAL_ADAPTER, NULL); + self->bus = gst_transcoder_get_message_bus (transcoder); + self->source = gst_bus_create_watch (self->bus); + + if (!self->source) { + GST_ERROR_OBJECT (transcoder, "Could not create watch."); + + gst_object_unref (self); + + return NULL; + } + + g_source_attach (self->source, context); + g_source_set_callback (self->source, + (GSourceFunc) gst_transcoder_signal_adapter_on_message, self, NULL); + return self; +} + +/** + * gst_transcoder_signal_adapter_new_sync_emit: + * @transcoder: (transfer none): #GstTranscoder instance to emit signals + * synchronously for. + * + * Returns: (transfer full): A new #GstTranscoderSignalAdapter to connect signal + * handlers to. + * + * Since: 1.20 + */ +GstTranscoderSignalAdapter * +gst_transcoder_signal_adapter_new_sync_emit (GstTranscoder * transcoder) +{ + GstBus *bus = NULL; + GstTranscoderSignalAdapter *self = NULL; + + g_return_val_if_fail (GST_IS_TRANSCODER (transcoder), NULL); + + bus = gst_transcoder_get_message_bus (transcoder); + + self = g_object_new (GST_TYPE_TRANSCODER_SIGNAL_ADAPTER, NULL); + self->bus = bus; + gst_bus_set_sync_handler (self->bus, + gst_transcoder_signal_adapter_bus_sync_handler, self, NULL); + return self; +} + +static void +gst_transcoder_signal_adapter_init (GstTranscoderSignalAdapter * self) +{ + self->source = NULL; +} + +static void +gst_transcoder_signal_adapter_dispose (GObject * object) +{ + GstTranscoderSignalAdapter *self = GST_TRANSCODER_SIGNAL_ADAPTER (object); + + if (self->source) { + g_source_destroy (self->source); + g_source_unref (self->source); + self->source = NULL; + } + + gst_clear_object (&self->bus); + gst_clear_object (&self->transcoder); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_transcoder_signal_adapter_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstTranscoderSignalAdapter *self = GST_TRANSCODER_SIGNAL_ADAPTER (object); + + switch (prop_id) { + case PROP_TRANSCODER: + g_value_set_object (value, self->transcoder); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_transcoder_signal_adapter_class_init (GstTranscoderSignalAdapterClass * + klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->dispose = gst_transcoder_signal_adapter_dispose; + gobject_class->get_property = gst_transcoder_signal_adapter_get_property; + + signals[SIGNAL_POSITION_UPDATED] = + g_signal_new ("position-updated", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL, + NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME); + + signals[SIGNAL_DURATION_CHANGED] = + g_signal_new ("duration-changed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL, + NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME); + + signals[SIGNAL_DONE] = + g_signal_new ("done", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL, + NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID); + + signals[SIGNAL_ERROR] = + g_signal_new ("error", 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_ERROR, GST_TYPE_STRUCTURE); + + signals[SIGNAL_WARNING] = + g_signal_new ("warning", 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_ERROR, GST_TYPE_STRUCTURE); + + /** + * GstTranscoderSignalAdapter:transcoder: + * + * The #GstTranscoder tracked by the adapter. + * + * Since: 1.20 + */ + param_specs[PROP_TRANSCODER] = + g_param_spec_object ("transcoder", "Transcoder", + "The GstTranscoder @self is tracking", GST_TYPE_TRANSCODER, + G_PARAM_READABLE); + + g_object_class_install_properties (gobject_class, PROP_LAST, param_specs); +} + + +/** + * gst_transcoder_signal_adapter_get_transcoder: + * @self: The #GstTranscoderSignalAdapter + * + * Returns: (transfer full): The #GstTranscoder @self is tracking + * + * Since: 1.20 + */ +GstTranscoder * +gst_transcoder_signal_adapter_get_transcoder (GstTranscoderSignalAdapter * self) +{ + return gst_object_ref (self->transcoder); +} diff --git a/gst-libs/gst/transcoder/gsttranscoder-signal-adapter.h b/gst-libs/gst/transcoder/gsttranscoder-signal-adapter.h new file mode 100644 index 0000000000..6dda124375 --- /dev/null +++ b/gst-libs/gst/transcoder/gsttranscoder-signal-adapter.h @@ -0,0 +1,61 @@ +/* GStreamer + * + * Copyright (C) 2019-2020 Stephan Hesse + * Copyright (C) 2020 Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#pragma once + +#include +#include + +G_BEGIN_DECLS + +/** + * GstTranscoderSignalAdapter: + * + * Transforms #GstTranscoder bus messages to signals from the adapter object. + * + * Since: 1.20 + */ + +/** + * GST_TYPE_TRANSCODER_SIGNAL_ADAPTER: + * + * Since: 1.20 + */ +#define GST_TYPE_TRANSCODER_SIGNAL_ADAPTER (gst_transcoder_signal_adapter_get_type ()) +GST_TRANSCODER_API + +/** + * GstTranscoderSignalAdapterClass: + * + * Since: 1.20 + */ +G_DECLARE_FINAL_TYPE(GstTranscoderSignalAdapter, gst_transcoder_signal_adapter, GST, TRANSCODER_SIGNAL_ADAPTER, GObject) + +GST_TRANSCODER_API +GstTranscoderSignalAdapter * gst_transcoder_signal_adapter_new_sync_emit (GstTranscoder * transcoder); + +GST_TRANSCODER_API +GstTranscoderSignalAdapter * gst_transcoder_signal_adapter_new (GstTranscoder * transcoder, GMainContext * context); + +GST_TRANSCODER_API +GstTranscoder * gst_transcoder_signal_adapter_get_transcoder (GstTranscoderSignalAdapter * self); + + +G_END_DECLS diff --git a/gst-libs/gst/transcoder/gsttranscoder.c b/gst-libs/gst/transcoder/gsttranscoder.c index ec70412378..2e7102274b 100644 --- a/gst-libs/gst/transcoder/gsttranscoder.c +++ b/gst-libs/gst/transcoder/gsttranscoder.c @@ -28,6 +28,7 @@ */ #include "gsttranscoder.h" +#include "gsttranscoder-message-private.h" GST_DEBUG_CATEGORY_STATIC (gst_transcoder_debug); #define GST_CAT_DEFAULT gst_transcoder_debug @@ -52,7 +53,6 @@ gst_transcoder_error_quark (void) enum { PROP_0, - PROP_SIGNAL_DISPATCHER, PROP_SRC_URI, PROP_DEST_URI, PROP_PROFILE, @@ -64,22 +64,10 @@ enum PROP_LAST }; -enum -{ - SIGNAL_POSITION_UPDATED, - SIGNAL_DURATION_CHANGED, - SIGNAL_DONE, - SIGNAL_ERROR, - SIGNAL_WARNING, - SIGNAL_LAST -}; - struct _GstTranscoder { GstObject parent; - GstTranscoderSignalDispatcher *signal_dispatcher; - GstEncodingProfile *profile; gchar *source_uri; gchar *dest_uri; @@ -99,6 +87,8 @@ struct _GstTranscoder gint wanted_cpu_usage; GstClockTime last_duration; + + GstBus *api_bus; }; struct _GstTranscoderClass @@ -106,15 +96,9 @@ struct _GstTranscoderClass GstObjectClass parent_class; }; -static void -gst_transcoder_signal_dispatcher_dispatch (GstTranscoderSignalDispatcher * self, - GstTranscoder * transcoder, void (*emitter) (gpointer data), gpointer data, - GDestroyNotify destroy); - #define parent_class gst_transcoder_parent_class G_DEFINE_TYPE (GstTranscoder, gst_transcoder, GST_TYPE_OBJECT); -static guint signals[SIGNAL_LAST] = { 0, }; static GParamSpec *param_specs[PROP_LAST] = { NULL, }; static void gst_transcoder_dispose (GObject * object); @@ -162,6 +146,7 @@ gst_transcoder_init (GstTranscoder * self) self->context = g_main_context_new (); self->loop = g_main_loop_new (self->context, FALSE); + self->api_bus = gst_bus_new (); self->wanted_cpu_usage = 100; self->position_update_interval_ms = DEFAULT_POSITION_UPDATE_INTERVAL_MS; @@ -180,12 +165,6 @@ gst_transcoder_class_init (GstTranscoderClass * klass) gobject_class->finalize = gst_transcoder_finalize; gobject_class->constructed = gst_transcoder_constructed; - param_specs[PROP_SIGNAL_DISPATCHER] = - g_param_spec_object ("signal-dispatcher", - "Signal Dispatcher", "Dispatcher for the signals to e.g. event loops", - GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - param_specs[PROP_SRC_URI] = g_param_spec_string ("src-uri", "URI", "Source URI", DEFAULT_URI, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); @@ -232,31 +211,6 @@ gst_transcoder_class_init (GstTranscoderClass * klass) DEFAULT_AVOID_REENCODING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, PROP_LAST, param_specs); - - signals[SIGNAL_POSITION_UPDATED] = - g_signal_new ("position-updated", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL, - NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME); - - signals[SIGNAL_DURATION_CHANGED] = - g_signal_new ("duration-changed", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL, - NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME); - - signals[SIGNAL_DONE] = - g_signal_new ("done", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL, - NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID); - - signals[SIGNAL_ERROR] = - g_signal_new ("error", 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_ERROR, GST_TYPE_STRUCTURE); - - signals[SIGNAL_WARNING] = - g_signal_new ("warning", 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_ERROR, GST_TYPE_STRUCTURE); } static void @@ -292,8 +246,6 @@ gst_transcoder_finalize (GObject * object) g_free (self->source_uri); g_free (self->dest_uri); - if (self->signal_dispatcher) - g_object_unref (self->signal_dispatcher); g_cond_clear (&self->cond); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -329,9 +281,6 @@ gst_transcoder_set_property (GObject * object, guint prop_id, GstTranscoder *self = GST_TRANSCODER (object); switch (prop_id) { - case PROP_SIGNAL_DISPATCHER: - self->signal_dispatcher = g_value_dup_object (value); - break; case PROP_SRC_URI:{ GST_OBJECT_LOCK (self); g_free (self->source_uri); @@ -441,6 +390,34 @@ gst_transcoder_get_property (GObject * object, guint prop_id, } } +/* + * Works same as gst_structure_set to set field/type/value triplets on message data + */ +static void +api_bus_post_message (GstTranscoder * self, GstTranscoderMessage message_type, + const gchar * firstfield, ...) +{ + GstStructure *message_data = NULL; + GstMessage *msg = NULL; + va_list varargs; + + GST_INFO ("Posting API-bus message-type: %s", + gst_transcoder_message_get_name (message_type)); + message_data = gst_structure_new (GST_TRANSCODER_MESSAGE_DATA, + GST_TRANSCODER_MESSAGE_DATA_TYPE, GST_TYPE_TRANSCODER_MESSAGE, + message_type, NULL); + + va_start (varargs, firstfield); + gst_structure_set_valist (message_data, firstfield, varargs); + va_end (varargs); + + msg = gst_message_new_custom (GST_MESSAGE_APPLICATION, + GST_OBJECT (self), message_data); + GST_DEBUG ("Created message with payload: [ %" GST_PTR_FORMAT " ]", + message_data); + gst_bus_post (self->api_bus, msg); +} + static gboolean main_loop_running_cb (gpointer user_data) { @@ -455,32 +432,6 @@ main_loop_running_cb (gpointer user_data) return G_SOURCE_REMOVE; } -typedef struct -{ - GstTranscoder *transcoder; - GstClockTime position; -} PositionUpdatedSignalData; - -static void -position_updated_dispatch (gpointer user_data) -{ - PositionUpdatedSignalData *data = user_data; - - if (data->transcoder->target_state >= GST_STATE_PAUSED) { - g_signal_emit (data->transcoder, signals[SIGNAL_POSITION_UPDATED], 0, - data->position); - g_object_notify_by_pspec (G_OBJECT (data->transcoder), - param_specs[PROP_POSITION]); - } -} - -static void -position_updated_signal_data_free (PositionUpdatedSignalData * data) -{ - g_object_unref (data->transcoder); - g_free (data); -} - static gboolean tick_cb (gpointer user_data) { @@ -498,16 +449,9 @@ tick_cb (gpointer user_data) GST_LOG_OBJECT (self, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS (position)); - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_POSITION_UPDATED], 0, NULL, NULL, NULL) != 0) { - PositionUpdatedSignalData *data = g_new0 (PositionUpdatedSignalData, 1); - - data->transcoder = g_object_ref (self); - data->position = position; - gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self, - position_updated_dispatch, data, - (GDestroyNotify) position_updated_signal_data_free); - } + api_bus_post_message (self, GST_TRANSCODER_MESSAGE_POSITION_UPDATED, + GST_TRANSCODER_MESSAGE_DATA_POSITION, GST_TYPE_CLOCK_TIME, position, + NULL); return G_SOURCE_CONTINUE; } @@ -537,58 +481,6 @@ remove_tick_source (GstTranscoder * self) self->tick_source = NULL; } -typedef struct -{ - GstTranscoder *transcoder; - GError *err; - GstStructure *details; -} IssueSignalData; - -static void -error_dispatch (gpointer user_data) -{ - IssueSignalData *data = user_data; - - g_signal_emit (data->transcoder, signals[SIGNAL_ERROR], 0, data->err, - data->details); -} - -static void -free_issue_signal_data (IssueSignalData * data) -{ - g_object_unref (data->transcoder); - if (data->details) - gst_structure_free (data->details); - g_clear_error (&data->err); - g_free (data); -} - -static void -emit_error (GstTranscoder * self, GError * err, const GstStructure * details) -{ - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_ERROR], 0, NULL, NULL, NULL) != 0) { - IssueSignalData *data = g_new0 (IssueSignalData, 1); - - data->transcoder = g_object_ref (self); - data->err = g_error_copy (err); - if (details) - data->details = gst_structure_copy (details); - gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self, - error_dispatch, data, (GDestroyNotify) free_issue_signal_data); - } - - g_error_free (err); - - remove_tick_source (self); - - self->target_state = GST_STATE_NULL; - self->current_state = GST_STATE_NULL; - self->is_live = FALSE; - self->is_eos = FALSE; - gst_element_set_state (self->transcodebin, GST_STATE_NULL); -} - static void dump_dot_file (GstTranscoder * self, const gchar * name) { @@ -602,33 +494,6 @@ dump_dot_file (GstTranscoder * self, const gchar * name) g_free (full_name); } -static void -warning_dispatch (gpointer user_data) -{ - IssueSignalData *data = user_data; - - g_signal_emit (data->transcoder, signals[SIGNAL_WARNING], 0, data->err, - data->details); -} - -static void -emit_warning (GstTranscoder * self, GError * err, const GstStructure * details) -{ - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_WARNING], 0, NULL, NULL, NULL) != 0) { - IssueSignalData *data = g_new0 (IssueSignalData, 1); - - data->transcoder = g_object_ref (self); - data->err = g_error_copy (err); - if (details) - data->details = gst_structure_copy (details); - gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self, - warning_dispatch, data, (GDestroyNotify) free_issue_signal_data); - } - - g_error_free (err); -} - static void error_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) { @@ -654,7 +519,11 @@ error_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) "msg-source-element-name", G_TYPE_STRING, "name", "msg-source-type", G_TYPE_GTYPE, G_OBJECT_TYPE (msg->src), "msg-error", G_TYPE_STRING, message, NULL); - emit_error (self, g_error_copy (err), details); + + api_bus_post_message (self, GST_TRANSCODER_MESSAGE_ERROR, + GST_TRANSCODER_MESSAGE_DATA_ERROR, G_TYPE_ERROR, err, + GST_TRANSCODER_MESSAGE_DATA_ISSUE_DETAILS, GST_TYPE_STRUCTURE, details, + NULL); gst_structure_free (details); g_clear_error (&err); @@ -695,8 +564,13 @@ warning_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) transcoder_err = g_error_new_literal (GST_TRANSCODER_ERROR, GST_TRANSCODER_ERROR_FAILED, full_message); - emit_warning (self, transcoder_err, details); + api_bus_post_message (self, GST_TRANSCODER_MESSAGE_WARNING, + GST_TRANSCODER_MESSAGE_DATA_WARNING, G_TYPE_ERROR, transcoder_err, + GST_TRANSCODER_MESSAGE_DATA_ISSUE_DETAILS, GST_TYPE_STRUCTURE, details, + NULL); + + g_clear_error (&transcoder_err); g_clear_error (&err); g_free (debug); g_free (name); @@ -704,12 +578,6 @@ warning_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) g_free (message); } -static void -eos_dispatch (gpointer user_data) -{ - g_signal_emit (user_data, signals[SIGNAL_DONE], 0); -} - static void eos_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg, gpointer user_data) @@ -723,11 +591,7 @@ eos_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg, tick_cb (self); remove_tick_source (self); - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_DONE], 0, NULL, NULL, NULL) != 0) { - gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self, - eos_dispatch, g_object_ref (self), (GDestroyNotify) g_object_unref); - } + api_bus_post_message (self, GST_TRANSCODER_MESSAGE_DONE, NULL, NULL); self->is_eos = TRUE; } @@ -744,54 +608,13 @@ clock_lost_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg, if (state_ret != GST_STATE_CHANGE_FAILURE) state_ret = gst_element_set_state (self->transcodebin, GST_STATE_PLAYING); - if (state_ret == GST_STATE_CHANGE_FAILURE) - emit_error (self, g_error_new (GST_TRANSCODER_ERROR, - GST_TRANSCODER_ERROR_FAILED, "Failed to handle clock loss"), - NULL); - } -} - -typedef struct -{ - GstTranscoder *transcoder; - GstClockTime duration; -} DurationChangedSignalData; - -static void -duration_changed_dispatch (gpointer user_data) -{ - DurationChangedSignalData *data = user_data; - - if (data->transcoder->target_state >= GST_STATE_PAUSED) { - g_signal_emit (data->transcoder, signals[SIGNAL_DURATION_CHANGED], 0, - data->duration); - g_object_notify_by_pspec (G_OBJECT (data->transcoder), - param_specs[PROP_DURATION]); - } -} - -static void -duration_changed_signal_data_free (DurationChangedSignalData * data) -{ - g_object_unref (data->transcoder); - g_free (data); -} - -static void -emit_duration_changed (GstTranscoder * self, GstClockTime duration) -{ - GST_DEBUG_OBJECT (self, "Duration changed %" GST_TIME_FORMAT, - GST_TIME_ARGS (duration)); - - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_DURATION_CHANGED], 0, NULL, NULL, NULL) != 0) { - DurationChangedSignalData *data = g_new0 (DurationChangedSignalData, 1); - - data->transcoder = g_object_ref (self); - data->duration = duration; - gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self, - duration_changed_dispatch, data, - (GDestroyNotify) duration_changed_signal_data_free); + if (state_ret == GST_STATE_CHANGE_FAILURE) { + GError *err = g_error_new (GST_TRANSCODER_ERROR, + GST_TRANSCODER_ERROR_FAILED, "Failed to handle clock loss"); + api_bus_post_message (self, GST_TRANSCODER_MESSAGE_ERROR, + GST_TRANSCODER_MESSAGE_DATA_ERROR, G_TYPE_ERROR, err, NULL); + g_error_free (err); + } } } @@ -836,7 +659,9 @@ duration_changed_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg, if (gst_element_query_duration (self->transcodebin, GST_FORMAT_TIME, &duration)) { - emit_duration_changed (self, duration); + api_bus_post_message (self, GST_TRANSCODER_MESSAGE_DURATION_CHANGED, + GST_TRANSCODER_MESSAGE_DATA_DURATION, GST_TYPE_CLOCK_TIME, + duration, NULL); } } @@ -866,11 +691,16 @@ request_state_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, self->target_state = state; state_ret = gst_element_set_state (self->transcodebin, state); - if (state_ret == GST_STATE_CHANGE_FAILURE) - emit_error (self, g_error_new (GST_TRANSCODER_ERROR, - GST_TRANSCODER_ERROR_FAILED, - "Failed to change to requested state %s", - gst_element_state_get_name (state)), NULL); + if (state_ret == GST_STATE_CHANGE_FAILURE) { + GError *err = g_error_new (GST_TRANSCODER_ERROR, + GST_TRANSCODER_ERROR_FAILED, + "Failed to change to requested state %s", + gst_element_state_get_name (state)); + + api_bus_post_message (self, GST_TRANSCODER_MESSAGE_ERROR, + GST_TRANSCODER_MESSAGE_DATA_ERROR, G_TYPE_ERROR, err, NULL); + g_error_free (err); + } } static void @@ -1030,7 +860,7 @@ gst_transcoder_new (const gchar * source_uri, profile = create_encoding_profile (encoding_profile); - return gst_transcoder_new_full (source_uri, dest_uri, profile, NULL); + return gst_transcoder_new_full (source_uri, dest_uri, profile); } /** @@ -1040,15 +870,12 @@ gst_transcoder_new (const gchar * source_uri, * @profile: The #GstEncodingProfile defining the output format * have a look at the #GstEncodingProfile documentation to find more * about the serialization format. - * @signal_dispatcher: The #GstTranscoderSignalDispatcher to be used - * to dispatch the various signals. * * Returns: a new #GstTranscoder instance */ GstTranscoder * gst_transcoder_new_full (const gchar * source_uri, - const gchar * dest_uri, GstEncodingProfile * profile, - GstTranscoderSignalDispatcher * signal_dispatcher) + const gchar * dest_uri, GstEncodingProfile * profile) { static GOnce once = G_ONCE_INIT; @@ -1058,8 +885,7 @@ gst_transcoder_new_full (const gchar * source_uri, g_return_val_if_fail (dest_uri, NULL); return g_object_new (GST_TYPE_TRANSCODER, "src-uri", source_uri, - "dest-uri", dest_uri, "profile", profile, - "signal-dispatcher", signal_dispatcher, NULL); + "dest-uri", dest_uri, "profile", profile, NULL); } typedef struct @@ -1069,8 +895,7 @@ typedef struct } RunSyncData; static void -_error_cb (GstTranscoder * self, GError * error, GstStructure * details, - RunSyncData * data) +_error_cb (RunSyncData * data, GError * error, GstStructure * details) { if (data->error == NULL) g_propagate_error (&data->error, error); @@ -1082,7 +907,7 @@ _error_cb (GstTranscoder * self, GError * error, GstStructure * details, } static void -_done_cb (GstTranscoder * self, RunSyncData * data) +_done_cb (RunSyncData * data) { if (data->loop) { g_main_loop_quit (data->loop); @@ -1103,17 +928,20 @@ gboolean gst_transcoder_run (GstTranscoder * self, GError ** error) { RunSyncData data = { 0, }; + GstTranscoderSignalAdapter *signal_adapter = + gst_transcoder_signal_adapter_new (self, NULL); data.loop = g_main_loop_new (NULL, FALSE); - g_signal_connect (self, "error", G_CALLBACK (_error_cb), &data); - g_signal_connect (self, "done", G_CALLBACK (_done_cb), &data); + g_signal_connect_swapped (signal_adapter, "error", G_CALLBACK (_error_cb), + &data); + g_signal_connect_swapped (signal_adapter, "done", G_CALLBACK (_done_cb), + &data); gst_transcoder_run_async (self); if (!data.error) g_main_loop_run (data.loop); - g_signal_handlers_disconnect_by_func (self, _error_cb, &data); - g_signal_handlers_disconnect_by_func (self, _done_cb, &data); + g_object_unref (signal_adapter); if (data.error) { if (error) @@ -1143,8 +971,12 @@ gst_transcoder_run_async (GstTranscoder * self) GST_DEBUG_OBJECT (self, "Play"); if (!self->profile) { - emit_error (self, g_error_new (GST_TRANSCODER_ERROR, - GST_TRANSCODER_ERROR_FAILED, "No \"profile\" provided"), NULL); + GError *err = g_error_new (GST_TRANSCODER_ERROR, + GST_TRANSCODER_ERROR_FAILED, "No \"profile\" provided"); + + api_bus_post_message (self, GST_TRANSCODER_MESSAGE_ERROR, + GST_TRANSCODER_MESSAGE_DATA_ERROR, G_TYPE_ERROR, err, NULL); + g_error_free (err); return; } @@ -1153,8 +985,12 @@ gst_transcoder_run_async (GstTranscoder * self) state_ret = gst_element_set_state (self->transcodebin, GST_STATE_PLAYING); if (state_ret == GST_STATE_CHANGE_FAILURE) { - emit_error (self, g_error_new (GST_TRANSCODER_ERROR, - GST_TRANSCODER_ERROR_FAILED, "Could not start transcoding"), NULL); + GError *err = g_error_new (GST_TRANSCODER_ERROR, + GST_TRANSCODER_ERROR_FAILED, "Could not start transcoding"); + api_bus_post_message (self, GST_TRANSCODER_MESSAGE_ERROR, + GST_TRANSCODER_MESSAGE_DATA_ERROR, G_TYPE_ERROR, err, NULL); + g_error_free (err); + return; } else if (state_ret == GST_STATE_CHANGE_NO_PREROLL) { self->is_live = TRUE; @@ -1372,212 +1208,154 @@ gst_transcoder_error_get_name (GstTranscoderError error) return NULL; } -G_DEFINE_INTERFACE (GstTranscoderSignalDispatcher, - gst_transcoder_signal_dispatcher, G_TYPE_OBJECT); - -static void -gst_transcoder_signal_dispatcher_default_init (G_GNUC_UNUSED - GstTranscoderSignalDispatcherInterface * iface) +/** + * gst_transcoder_get_message_bus: + * @transcoder: #GstTranscoder instance + * + * GstTranscoder API exposes a #GstBus instance which purpose is to provide data + * structures representing transcoder-internal events in form of #GstMessage-s of + * type GST_MESSAGE_APPLICATION. + * + * Each message carries a "transcoder-message" field of type #GstTranscoderMessage. + * Further fields of the message data are specific to each possible value of + * that enumeration. + * + * Applications can consume the messages asynchronously within their own + * event-loop / UI-thread etc. Note that in case the application does not + * consume the messages, the bus will accumulate these internally and eventually + * fill memory. To avoid that, the bus has to be set "flushing". + * + * Returns: (transfer full): The transcoder message bus instance + * + * Since: 1.20 + */ +GstBus * +gst_transcoder_get_message_bus (GstTranscoder * self) { - + return g_object_ref (self->api_bus); } -static void -gst_transcoder_signal_dispatcher_dispatch (GstTranscoderSignalDispatcher * self, - GstTranscoder * transcoder, void (*emitter) (gpointer data), gpointer data, - GDestroyNotify destroy) -{ - GstTranscoderSignalDispatcherInterface *iface; - - if (!self) { - emitter (data); - if (destroy) - destroy (data); - return; - } - - g_return_if_fail (GST_IS_TRANSCODER_SIGNAL_DISPATCHER (self)); - iface = GST_TRANSCODER_SIGNAL_DISPATCHER_GET_INTERFACE (self); - g_return_if_fail (iface->dispatch != NULL); - - iface->dispatch (self, transcoder, emitter, data, destroy); -} - -struct _GstTranscoderGMainContextSignalDispatcher -{ - GObject parent; - GMainContext *application_context; -}; - -struct _GstTranscoderGMainContextSignalDispatcherClass -{ - GObjectClass parent_class; -}; - -static void - gst_transcoder_g_main_context_signal_dispatcher_interface_init - (GstTranscoderSignalDispatcherInterface * iface); - -enum -{ - G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_0, - G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT, - G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST -}; - -G_DEFINE_TYPE_WITH_CODE (GstTranscoderGMainContextSignalDispatcher, - gst_transcoder_g_main_context_signal_dispatcher, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER, - gst_transcoder_g_main_context_signal_dispatcher_interface_init)); - -static GParamSpec - * g_main_context_signal_dispatcher_param_specs - [G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST] = { NULL, }; - -static void -gst_transcoder_g_main_context_signal_dispatcher_finalize (GObject * object) -{ - GstTranscoderGMainContextSignalDispatcher *self = - GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object); - - if (self->application_context) - g_main_context_unref (self->application_context); - - G_OBJECT_CLASS - (gst_transcoder_g_main_context_signal_dispatcher_parent_class)->finalize - (object); -} - -static void -gst_transcoder_g_main_context_signal_dispatcher_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec) -{ - GstTranscoderGMainContextSignalDispatcher *self = - GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object); - - switch (prop_id) { - case G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT: - self->application_context = g_value_dup_boxed (value); - if (!self->application_context) - self->application_context = g_main_context_ref_thread_default (); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_transcoder_g_main_context_signal_dispatcher_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec) -{ - GstTranscoderGMainContextSignalDispatcher *self = - GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object); - - switch (prop_id) { - case G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT: - g_value_set_boxed (value, self->application_context); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void - gst_transcoder_g_main_context_signal_dispatcher_class_init - (GstTranscoderGMainContextSignalDispatcherClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->finalize = - gst_transcoder_g_main_context_signal_dispatcher_finalize; - gobject_class->set_property = - gst_transcoder_g_main_context_signal_dispatcher_set_property; - gobject_class->get_property = - gst_transcoder_g_main_context_signal_dispatcher_get_property; - - g_main_context_signal_dispatcher_param_specs - [G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT] = - g_param_spec_boxed ("application-context", "Application Context", - "Application GMainContext to dispatch signals to", G_TYPE_MAIN_CONTEXT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (gobject_class, - G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST, - g_main_context_signal_dispatcher_param_specs); -} - -static void - gst_transcoder_g_main_context_signal_dispatcher_init - (G_GNUC_UNUSED GstTranscoderGMainContextSignalDispatcher * self) -{ -} - -typedef struct -{ - void (*emitter) (gpointer data); - gpointer data; - GDestroyNotify destroy; -} GMainContextSignalDispatcherData; - -static gboolean -g_main_context_signal_dispatcher_dispatch_gsourcefunc (gpointer user_data) -{ - GMainContextSignalDispatcherData *data = user_data; - - data->emitter (data->data); - - return G_SOURCE_REMOVE; -} - -static void -g_main_context_signal_dispatcher_dispatch_destroy (gpointer user_data) -{ - GMainContextSignalDispatcherData *data = user_data; - - if (data->destroy) - data->destroy (data->data); - g_free (data); -} - -/* *INDENT-OFF* */ -static void -gst_transcoder_g_main_context_signal_dispatcher_dispatch (GstTranscoderSignalDispatcher * iface, - G_GNUC_UNUSED GstTranscoder * transcoder, void (*emitter) (gpointer data), - gpointer data, GDestroyNotify destroy) -{ - GstTranscoderGMainContextSignalDispatcher *self = - GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (iface); - GMainContextSignalDispatcherData *gsourcefunc_data = - g_new0 (GMainContextSignalDispatcherData, 1); - - gsourcefunc_data->emitter = emitter; - gsourcefunc_data->data = data; - gsourcefunc_data->destroy = destroy; - - g_main_context_invoke_full (self->application_context, - G_PRIORITY_DEFAULT, g_main_context_signal_dispatcher_dispatch_gsourcefunc, - gsourcefunc_data, g_main_context_signal_dispatcher_dispatch_destroy); -} - -static void -gst_transcoder_g_main_context_signal_dispatcher_interface_init (GstTranscoderSignalDispatcherInterface * iface) -{ - iface->dispatch = gst_transcoder_g_main_context_signal_dispatcher_dispatch; -} -/* *INDENT-ON* */ - /** - * gst_transcoder_g_main_context_signal_dispatcher_new: - * @application_context: (allow-none): GMainContext to use or %NULL + * gst_transcoder_message_get_name: + * @message: a #GstTranscoderMessage * - * Returns: (transfer full): + * Returns (transfer none): The message name + * + * Since: 1.20 */ -GstTranscoderSignalDispatcher * -gst_transcoder_g_main_context_signal_dispatcher_new (GMainContext * - application_context) +const gchar * +gst_transcoder_message_get_name (GstTranscoderMessage message) { - return g_object_new (GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER, - "application-context", application_context, NULL); + GEnumClass *enum_class; + GEnumValue *enum_value; + enum_class = g_type_class_ref (GST_TYPE_TRANSCODER_MESSAGE); + enum_value = g_enum_get_value (enum_class, message); + g_assert (enum_value != NULL); + g_type_class_unref (enum_class); + return enum_value->value_name; +} + + +#define PARSE_MESSAGE_FIELD(msg, field, value_type, value) G_STMT_START { \ + const GstStructure *data = NULL; \ + g_return_if_fail (gst_transcoder_is_transcoder_message (msg)); \ + data = gst_message_get_structure (msg); \ + if (!gst_structure_get (data, field, value_type, value, NULL)) { \ + g_error ("Could not parse field from structure: %s", field); \ + } \ +} G_STMT_END + +/** + * gst_transcoder_is_transcoder_message: + * @msg: A #GstMessage + * + * Returns: A #gboolean indicating whether the passes message represents a #GstTranscoder message or not. + * + * Since: 1.20 + */ +gboolean +gst_transcoder_is_transcoder_message (GstMessage * msg) +{ + const GstStructure *data = NULL; + g_return_val_if_fail (GST_IS_MESSAGE (msg), FALSE); + + data = gst_message_get_structure (msg); + g_return_val_if_fail (data, FALSE); + + return g_str_equal (gst_structure_get_name (data), + GST_TRANSCODER_MESSAGE_DATA); +} + +/** + * gst_transcoder_message_parse_duration: + * @msg: A #GstMessage + * @duration: (out): the resulting duration + * + * Parse the given duration @msg and extract the corresponding #GstClockTime + * + * Since: 1.20 + */ +void +gst_transcoder_message_parse_duration (GstMessage * msg, + GstClockTime * duration) +{ + PARSE_MESSAGE_FIELD (msg, GST_TRANSCODER_MESSAGE_DATA_DURATION, + GST_TYPE_CLOCK_TIME, duration); +} + +/** + * gst_transcoder_message_parse_position: + * @msg: A #GstMessage + * @position: (out): the resulting position + * + * Parse the given position @msg and extract the corresponding #GstClockTime + * + * Since: 1.20 + */ +void +gst_transcoder_message_parse_position (GstMessage * msg, + GstClockTime * position) +{ + PARSE_MESSAGE_FIELD (msg, GST_TRANSCODER_MESSAGE_DATA_POSITION, + GST_TYPE_CLOCK_TIME, position); +} + +/** + * gst_transcoder_message_parse_error: + * @msg: A #GstMessage + * @error: (out): the resulting error + * @details: (out): (transfer none): A GstStructure containing extra details about the error + * + * Parse the given error @msg and extract the corresponding #GError + * + * Since: 1.20 + */ +void +gst_transcoder_message_parse_error (GstMessage * msg, GError * error, + GstStructure ** details) +{ + PARSE_MESSAGE_FIELD (msg, GST_TRANSCODER_MESSAGE_DATA_ERROR, G_TYPE_ERROR, + error); + PARSE_MESSAGE_FIELD (msg, GST_TRANSCODER_MESSAGE_DATA_ISSUE_DETAILS, + GST_TYPE_STRUCTURE, details); +} + +/** + * gst_transcoder_message_parse_warning: + * @msg: A #GstMessage + * @error: (out): the resulting warning + * @details: (out): (transfer none): A GstStructure containing extra details about the warning + * + * Parse the given error @msg and extract the corresponding #GError warning + * + * Since: 1.20 + */ +void +gst_transcoder_message_parse_warning (GstMessage * msg, GError * error, + GstStructure ** details) +{ + PARSE_MESSAGE_FIELD (msg, GST_TRANSCODER_MESSAGE_DATA_WARNING, G_TYPE_ERROR, + error); + PARSE_MESSAGE_FIELD (msg, GST_TRANSCODER_MESSAGE_DATA_ISSUE_DETAILS, + GST_TYPE_STRUCTURE, details); } diff --git a/gst-libs/gst/transcoder/gsttranscoder.h b/gst-libs/gst/transcoder/gsttranscoder.h index ccc3dd7fa4..dcd22860d7 100644 --- a/gst-libs/gst/transcoder/gsttranscoder.h +++ b/gst-libs/gst/transcoder/gsttranscoder.h @@ -12,9 +12,6 @@ G_BEGIN_DECLS -typedef struct _GstTranscoderSignalDispatcher GstTranscoderSignalDispatcher; -typedef struct _GstTranscoderSignalDispatcherInterface GstTranscoderSignalDispatcherInterface; - /*********** Error definitions ************/ #define GST_TRANSCODER_ERROR (gst_transcoder_error_quark ()) @@ -31,6 +28,51 @@ GQuark gst_transcoder_error_quark (void); GST_TRANSCODER_API const gchar * gst_transcoder_error_get_name (GstTranscoderError error); +/*********** Messages types definitions ************/ + +/** + * GstTranscoderMessage: + * @GST_TRANSCODER_MESSAGE_POSITION_UPDATED: Sink position changed + * @GST_TRANSCODER_MESSAGE_DURATION_CHANGED: Duration of stream changed + * @GST_TRANSCODER_MESSAGE_DONE: Transcoding is done + * @GST_TRANSCODER_MESSAGE_ERROR: Message contains an error + * @GST_TRANSCODER_MESSAGE_WARNING: Message contains an error + * + * Types of messages that will be posted on the transcoder API bus. + * + * See also #gst_transcoder_get_message_bus() + * + * Since: 1.20 + */ +typedef enum +{ + GST_TRANSCODER_MESSAGE_POSITION_UPDATED, + GST_TRANSCODER_MESSAGE_DURATION_CHANGED, + GST_TRANSCODER_MESSAGE_DONE, + GST_TRANSCODER_MESSAGE_ERROR, + GST_TRANSCODER_MESSAGE_WARNING, +} GstTranscoderMessage; + +GST_TRANSCODER_API +gboolean gst_transcoder_is_transcoder_message (GstMessage * msg); + +GST_TRANSCODER_API +const gchar * gst_transcoder_message_get_name (GstTranscoderMessage message); + +GST_TRANSCODER_API +void gst_transcoder_message_parse_position (GstMessage * msg, GstClockTime * position); + +GST_TRANSCODER_API +void gst_transcoder_message_parse_duration (GstMessage * msg, GstClockTime * duration); + +GST_TRANSCODER_API +void gst_transcoder_message_parse_error (GstMessage * msg, GError * error, GstStructure ** details); + +GST_TRANSCODER_API +void gst_transcoder_message_parse_warning (GstMessage * msg, GError * error, GstStructure ** details); + + + /*********** GstTranscoder definition ************/ #define GST_TYPE_TRANSCODER (gst_transcoder_get_type ()) @@ -50,22 +92,24 @@ GstTranscoder * gst_transcoder_new (const gchar * source_ GST_TRANSCODER_API GstTranscoder * gst_transcoder_new_full (const gchar * source_uri, const gchar * dest_uri, - GstEncodingProfile *profile, - GstTranscoderSignalDispatcher *signal_dispatcher); + GstEncodingProfile * profile); GST_TRANSCODER_API -gboolean gst_transcoder_run (GstTranscoder *self, +gboolean gst_transcoder_run (GstTranscoder * self, GError ** error); GST_TRANSCODER_API -void gst_transcoder_set_cpu_usage (GstTranscoder *self, +GstBus * gst_transcoder_get_message_bus (GstTranscoder * transcoder); + +GST_TRANSCODER_API +void gst_transcoder_set_cpu_usage (GstTranscoder * self, gint cpu_usage); GST_TRANSCODER_API -void gst_transcoder_run_async (GstTranscoder *self); +void gst_transcoder_run_async (GstTranscoder * self); GST_TRANSCODER_API -void gst_transcoder_set_position_update_interval (GstTranscoder *self, +void gst_transcoder_set_position_update_interval (GstTranscoder * self, guint interval); GST_TRANSCODER_API @@ -75,7 +119,7 @@ GST_TRANSCODER_API gchar * gst_transcoder_get_dest_uri (GstTranscoder * self); GST_TRANSCODER_API -guint gst_transcoder_get_position_update_interval (GstTranscoder *self); +guint gst_transcoder_get_position_update_interval (GstTranscoder * self); GST_TRANSCODER_API GstClockTime gst_transcoder_get_position (GstTranscoder * self); @@ -92,43 +136,7 @@ GST_TRANSCODER_API void gst_transcoder_set_avoid_reencoding (GstTranscoder * self, gboolean avoid_reencoding); - -/****************** Signal dispatcher *******************************/ - -#define GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER (gst_transcoder_signal_dispatcher_get_type ()) -#define GST_TRANSCODER_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER, GstTranscoderSignalDispatcher)) -#define GST_IS_TRANSCODER_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER)) -#define GST_TRANSCODER_SIGNAL_DISPATCHER_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER, GstTranscoderSignalDispatcherInterface)) - -struct _GstTranscoderSignalDispatcherInterface { - GTypeInterface parent_iface; - - void (*dispatch) (GstTranscoderSignalDispatcher * self, - GstTranscoder * transcoder, - void (*emitter) (gpointer data), - gpointer data, - GDestroyNotify destroy); -}; - -typedef struct _GstTranscoderGMainContextSignalDispatcher GstTranscoderGMainContextSignalDispatcher; -typedef struct _GstTranscoderGMainContextSignalDispatcherClass GstTranscoderGMainContextSignalDispatcherClass; - -GST_TRANSCODER_API -GType gst_transcoder_signal_dispatcher_get_type (void); - -#define GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (gst_transcoder_g_main_context_signal_dispatcher_get_type ()) -#define GST_IS_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER)) -#define GST_IS_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER)) -#define GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER, GstTranscoderGMainContextSignalDispatcherClass)) -#define GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER, GstTranscoderGMainContextSignalDispatcher)) -#define GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER, GstTranscoderGMainContextSignalDispatcherClass)) -#define GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CAST(obj) ((GstTranscoderGMainContextSignalDispatcher*)(obj)) - -GST_TRANSCODER_API -GType gst_transcoder_g_main_context_signal_dispatcher_get_type (void); - -GST_TRANSCODER_API -GstTranscoderSignalDispatcher * gst_transcoder_g_main_context_signal_dispatcher_new (GMainContext * application_context); +#include "gsttranscoder-signal-adapter.h" G_END_DECLS diff --git a/gst-libs/gst/transcoder/meson.build b/gst-libs/gst/transcoder/meson.build index 4381efa9e4..b6c54983f2 100644 --- a/gst-libs/gst/transcoder/meson.build +++ b/gst-libs/gst/transcoder/meson.build @@ -1,5 +1,5 @@ -sources = files(['gsttranscoder.c']) -headers = files(['gsttranscoder.h', 'transcoder-prelude.h']) +sources = files(['gsttranscoder.c', 'gsttranscoder-signal-adapter.c']) +headers = files(['gsttranscoder.h', 'transcoder-prelude.h', 'gsttranscoder-signal-adapter.h']) install_headers(headers, subdir : 'gstreamer-' + api_version + '/gst/transcoder') diff --git a/tools/gst-transcoder.c b/tools/gst-transcoder.c index d805c7649b..3d1bc83d22 100644 --- a/tools/gst-transcoder.c +++ b/tools/gst-transcoder.c @@ -282,6 +282,7 @@ main (int argc, char *argv[]) GError *err = NULL; GstTranscoder *transcoder; GOptionContext *ctx; + GstTranscoderSignalAdapter *signal_adapter; Settings settings = { .cpu_usage = 100, .rate = -1, @@ -371,19 +372,22 @@ main (int argc, char *argv[]) } transcoder = gst_transcoder_new_full (settings.src_uri, settings.dest_uri, - settings.profile, NULL); + settings.profile); gst_transcoder_set_avoid_reencoding (transcoder, TRUE); - gst_transcoder_set_cpu_usage (transcoder, settings.cpu_usage); - g_signal_connect (transcoder, "position-updated", - G_CALLBACK (position_updated_cb), NULL); - g_signal_connect (transcoder, "warning", G_CALLBACK (_warning_cb), NULL); - g_signal_connect (transcoder, "error", G_CALLBACK (_error_cb), NULL); - g_assert (transcoder); + signal_adapter = gst_transcoder_signal_adapter_new (transcoder, NULL); + g_signal_connect_swapped (signal_adapter, "position-updated", + G_CALLBACK (position_updated_cb), transcoder); + g_signal_connect_swapped (signal_adapter, "warning", G_CALLBACK (_warning_cb), + transcoder); + g_signal_connect_swapped (signal_adapter, "error", G_CALLBACK (_error_cb), + transcoder); + ok ("Starting transcoding..."); gst_transcoder_run (transcoder, &err); + g_object_unref (signal_adapter); if (!err) ok ("\nDONE.");