1 /* GStreamer
2 *
3 * Copyright (C) 2019-2020 Stephan Hesse <stephan@emliri.com>
4 * Copyright (C) 2020 Thibault Saunier <tsaunier@igalia.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "gsttranscoder.h"
27 #include "gsttranscoder-signal-adapter.h"
28
29 #include "gsttranscoder-private.h"
30
31 #include <gst/gst.h>
32
33 GST_DEBUG_CATEGORY_STATIC (gst_transcoder_signal_adapter_debug);
34 #define GST_CAT_DEFAULT gst_transcoder_signal_adapter_debug
35
36 enum
37 {
38 SIGNAL_POSITION_UPDATED,
39 SIGNAL_DURATION_CHANGED,
40 SIGNAL_STATE_CHANGED,
41 SIGNAL_DONE,
42 SIGNAL_ERROR,
43 SIGNAL_WARNING,
44 SIGNAL_LAST
45 };
46
47 enum
48 {
49 PROP_0,
50 PROP_TRANSCODER,
51 PROP_LAST
52 };
53
54 static GParamSpec *param_specs[PROP_LAST] = { NULL, };
55
56 struct _GstTranscoderSignalAdapterClass
57 {
58 GObjectClass parent_class;
59 };
60
61 #define _do_init \
62 GST_DEBUG_CATEGORY_INIT (gst_transcoder_signal_adapter_debug, "gst-transcoder-signaladapter", \
63 0, "GstTranscoder signal adapter")
64
65 #define parent_class gst_transcoder_signal_adapter_parent_class
66 G_DEFINE_TYPE_WITH_CODE (GstTranscoderSignalAdapter,
67 gst_transcoder_signal_adapter, G_TYPE_OBJECT, _do_init);
68
69 static guint signals[SIGNAL_LAST] = { 0, };
70
71 static void
gst_transcoder_signal_adapter_emit(GstTranscoderSignalAdapter * self,const GstStructure * message_data)72 gst_transcoder_signal_adapter_emit (GstTranscoderSignalAdapter * self,
73 const GstStructure * message_data)
74 {
75 GstTranscoderMessage transcoder_message_type;
76 g_return_if_fail (g_str_equal (gst_structure_get_name (message_data),
77 GST_TRANSCODER_MESSAGE_DATA));
78
79 GST_LOG ("Emitting message %" GST_PTR_FORMAT, message_data);
80 gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_TYPE,
81 GST_TYPE_TRANSCODER_MESSAGE, &transcoder_message_type, NULL);
82
83 switch (transcoder_message_type) {
84 case GST_TRANSCODER_MESSAGE_POSITION_UPDATED:{
85 GstClockTime pos = GST_CLOCK_TIME_NONE;
86 gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_POSITION,
87 GST_TYPE_CLOCK_TIME, &pos, NULL);
88 g_signal_emit (self, signals[SIGNAL_POSITION_UPDATED], 0, pos);
89 break;
90 }
91 case GST_TRANSCODER_MESSAGE_DURATION_CHANGED:{
92 GstClockTime duration = GST_CLOCK_TIME_NONE;
93 gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_DURATION,
94 GST_TYPE_CLOCK_TIME, &duration, NULL);
95 g_signal_emit (self, signals[SIGNAL_DURATION_CHANGED], 0, duration);
96 break;
97 }
98 case GST_TRANSCODER_MESSAGE_STATE_CHANGED:{
99 GstTranscoderState state;
100 gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_STATE,
101 GST_TYPE_TRANSCODER_STATE, &state, NULL);
102 g_signal_emit (self, signals[SIGNAL_STATE_CHANGED], 0, state);
103 break;
104 }
105 case GST_TRANSCODER_MESSAGE_DONE:
106 g_signal_emit (self, signals[SIGNAL_DONE], 0);
107 break;
108 case GST_TRANSCODER_MESSAGE_ERROR:{
109 GError *error = NULL;
110 GstStructure *details = NULL;
111
112 gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_ERROR,
113 G_TYPE_ERROR, &error, GST_TYPE_STRUCTURE, &details, NULL);
114 g_signal_emit (self, signals[SIGNAL_ERROR], 0, error, details);
115 g_error_free (error);
116 if (details)
117 gst_structure_free (details);
118 break;
119 }
120 case GST_TRANSCODER_MESSAGE_WARNING:{
121 GstStructure *details = NULL;
122 GError *error = NULL;
123
124 gst_structure_get (message_data, GST_TRANSCODER_MESSAGE_DATA_WARNING,
125 G_TYPE_ERROR, &error, GST_TYPE_STRUCTURE, &details, NULL);
126 g_signal_emit (self, signals[SIGNAL_WARNING], 0, error, details);
127 g_error_free (error);
128 if (details)
129 gst_structure_free (details);
130 break;
131 }
132 default:
133 g_assert_not_reached ();
134 break;
135 }
136 }
137
138 /*
139 * callback for the bus-message in-sync handling
140 */
141 static GstBusSyncReply
gst_transcoder_signal_adapter_bus_sync_handler(GstBus * bus,GstMessage * message,gpointer user_data)142 gst_transcoder_signal_adapter_bus_sync_handler
143 (GstBus * bus, GstMessage * message, gpointer user_data)
144 {
145 GstTranscoderSignalAdapter *self = GST_TRANSCODER_SIGNAL_ADAPTER (user_data);
146 const GstStructure *message_data = gst_message_get_structure (message);
147 gst_transcoder_signal_adapter_emit (self, message_data);
148 gst_message_unref (message);
149 return GST_BUS_DROP;
150 }
151
152 /*
153 * callback for the bus-watch
154 * pre: there is a message on the bus
155 */
156 static gboolean
gst_transcoder_signal_adapter_on_message(GstBus * bus,GstMessage * message,gpointer user_data)157 gst_transcoder_signal_adapter_on_message (GstBus * bus,
158 GstMessage * message, gpointer user_data)
159 {
160 GstTranscoderSignalAdapter *self = GST_TRANSCODER_SIGNAL_ADAPTER (user_data);
161 const GstStructure *message_data = gst_message_get_structure (message);
162 gst_transcoder_signal_adapter_emit (self, message_data);
163 return TRUE;
164 }
165
166 /**
167 * gst_transcoder_signal_adapter_new:
168 * @transcoder: (transfer none): #GstTranscoder instance to emit signals for.
169 * @context: (nullable): A #GMainContext on which the main-loop will process
170 * transcoder bus messages on. Can be NULL (thread-default
171 * context will be used then).
172 *
173 * A bus-watching #GSource will be created and attached to the context. The
174 * attached callback will emit the corresponding signal for the message
175 * received. Matching signals for transcoder messages from the bus will be
176 * emitted by it on the created adapter object.
177 *
178 * Returns: (transfer full)(nullable): A new #GstTranscoderSignalAdapter to
179 * connect signal handlers to.
180 *
181 * Since: 1.20
182 */
183 GstTranscoderSignalAdapter *
gst_transcoder_signal_adapter_new(GstTranscoder * transcoder,GMainContext * context)184 gst_transcoder_signal_adapter_new (GstTranscoder * transcoder,
185 GMainContext * context)
186 {
187 GstTranscoderSignalAdapter *self = NULL;
188
189 g_return_val_if_fail (GST_IS_TRANSCODER (transcoder), NULL);
190
191 self = g_object_new (GST_TYPE_TRANSCODER_SIGNAL_ADAPTER, NULL);
192 self->bus = gst_transcoder_get_message_bus (transcoder);
193 self->source = gst_bus_create_watch (self->bus);
194
195 if (!self->source) {
196 GST_ERROR_OBJECT (transcoder, "Could not create watch.");
197
198 gst_object_unref (self);
199
200 return NULL;
201 }
202
203 g_weak_ref_set (&self->transcoder, transcoder);
204 g_source_attach (self->source, context);
205 g_source_set_callback (self->source,
206 (GSourceFunc) gst_transcoder_signal_adapter_on_message, self, NULL);
207 return self;
208 }
209
210 /**
211 * gst_transcoder_signal_adapter_new_sync_emit:
212 * @transcoder: (transfer none): #GstTranscoder instance to emit signals
213 * synchronously for.
214 *
215 * Returns: (transfer full): A new #GstTranscoderSignalAdapter to connect signal
216 * handlers to.
217 *
218 * Since: 1.20
219 */
220 GstTranscoderSignalAdapter *
gst_transcoder_signal_adapter_new_sync_emit(GstTranscoder * transcoder)221 gst_transcoder_signal_adapter_new_sync_emit (GstTranscoder * transcoder)
222 {
223 GstBus *bus = NULL;
224 GstTranscoderSignalAdapter *self = NULL;
225
226 g_return_val_if_fail (GST_IS_TRANSCODER (transcoder), NULL);
227
228 bus = gst_transcoder_get_message_bus (transcoder);
229
230 self = g_object_new (GST_TYPE_TRANSCODER_SIGNAL_ADAPTER, NULL);
231 self->bus = bus;
232 gst_bus_set_sync_handler (self->bus,
233 gst_transcoder_signal_adapter_bus_sync_handler, self, NULL);
234 return self;
235 }
236
237 static void
gst_transcoder_signal_adapter_init(GstTranscoderSignalAdapter * self)238 gst_transcoder_signal_adapter_init (GstTranscoderSignalAdapter * self)
239 {
240 self->source = NULL;
241 }
242
243 static void
gst_transcoder_signal_adapter_dispose(GObject * object)244 gst_transcoder_signal_adapter_dispose (GObject * object)
245 {
246 GstTranscoderSignalAdapter *self = GST_TRANSCODER_SIGNAL_ADAPTER (object);
247
248 if (self->source) {
249 g_source_destroy (self->source);
250 g_source_unref (self->source);
251 self->source = NULL;
252 }
253
254 gst_clear_object (&self->bus);
255
256 G_OBJECT_CLASS (parent_class)->dispose (object);
257 }
258
259 static void
gst_transcoder_signal_adapter_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)260 gst_transcoder_signal_adapter_get_property (GObject * object, guint prop_id,
261 GValue * value, GParamSpec * pspec)
262 {
263 GstTranscoderSignalAdapter *self = GST_TRANSCODER_SIGNAL_ADAPTER (object);
264
265 switch (prop_id) {
266 case PROP_TRANSCODER:
267 g_value_take_object (value, g_weak_ref_get (&self->transcoder));
268 break;
269 default:
270 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
271 break;
272 }
273 }
274
275 static void
gst_transcoder_signal_adapter_class_init(GstTranscoderSignalAdapterClass * klass)276 gst_transcoder_signal_adapter_class_init (GstTranscoderSignalAdapterClass *
277 klass)
278 {
279 GObjectClass *gobject_class = (GObjectClass *) klass;
280
281 gobject_class->dispose = gst_transcoder_signal_adapter_dispose;
282 gobject_class->get_property = gst_transcoder_signal_adapter_get_property;
283
284 signals[SIGNAL_POSITION_UPDATED] =
285 g_signal_new ("position-updated", G_TYPE_FROM_CLASS (klass),
286 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
287 NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME);
288
289 signals[SIGNAL_DURATION_CHANGED] =
290 g_signal_new ("duration-changed", G_TYPE_FROM_CLASS (klass),
291 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
292 NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME);
293
294 signals[SIGNAL_DONE] =
295 g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
296 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
297 NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID);
298
299 signals[SIGNAL_ERROR] =
300 g_signal_new ("error", G_TYPE_FROM_CLASS (klass),
301 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
302 NULL, NULL, G_TYPE_NONE, 2, G_TYPE_ERROR, GST_TYPE_STRUCTURE);
303
304 signals[SIGNAL_WARNING] =
305 g_signal_new ("warning", G_TYPE_FROM_CLASS (klass),
306 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
307 NULL, NULL, G_TYPE_NONE, 2, G_TYPE_ERROR, GST_TYPE_STRUCTURE);
308
309 signals[SIGNAL_STATE_CHANGED] =
310 g_signal_new ("state-changed", G_TYPE_FROM_CLASS (klass),
311 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
312 NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_TRANSCODER_STATE);
313
314 /**
315 * GstTranscoderSignalAdapter:transcoder:
316 *
317 * The #GstTranscoder tracked by the adapter.
318 *
319 * Since: 1.20
320 */
321 param_specs[PROP_TRANSCODER] =
322 g_param_spec_object ("transcoder", "Transcoder",
323 "The GstTranscoder @self is tracking", GST_TYPE_TRANSCODER,
324 G_PARAM_READABLE);
325
326 g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
327 }
328
329
330 /**
331 * gst_transcoder_signal_adapter_get_transcoder:
332 * @self: The #GstTranscoderSignalAdapter
333 *
334 * Returns: (transfer full)(nullable): The #GstTranscoder @self is tracking
335 *
336 * Since: 1.20
337 */
338 GstTranscoder *
gst_transcoder_signal_adapter_get_transcoder(GstTranscoderSignalAdapter * self)339 gst_transcoder_signal_adapter_get_transcoder (GstTranscoderSignalAdapter * self)
340 {
341 return g_weak_ref_get (&self->transcoder);
342 }
343