Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Deniz Ugur, Romain Bouqueau, Sohaib Larbi
5 : * Copyright (c) Motion Spell
6 : * All rights reserved
7 : *
8 : * This file is part of the GPAC/GStreamer wrapper
9 : *
10 : * This GPAC/GStreamer wrapper is free software; you can redistribute it
11 : * and/or modify it under the terms of the GNU Affero General Public License
12 : * as published by the Free Software Foundation; either version 3, or (at
13 : * your option) any later version.
14 : *
15 : * This GPAC/GStreamer wrapper is distributed in the hope that it will be
16 : * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Affero General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Affero General Public
21 : * License along with this library; see the file LICENSE. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #include "elements/gstgpacsink.h"
27 :
28 : GST_DEBUG_CATEGORY_STATIC(gst_gpac_sink_debug);
29 : #define GST_CAT_DEFAULT gst_gpac_sink_debug
30 :
31 : // Define the element properties
32 : #define gst_gpac_sink_parent_class parent_class
33 61 : G_DEFINE_TYPE(GstGpacSink, gst_gpac_sink, GST_TYPE_BIN);
34 :
35 : // #MARK: Properties
36 : static void
37 6 : gst_gpac_sink_set_property(GObject* object,
38 : guint prop_id,
39 : const GValue* value,
40 : GParamSpec* pspec)
41 : {
42 6 : GstGpacSink* gpac_sink = GST_GPAC_SINK(object);
43 6 : g_return_if_fail(GST_IS_GPAC_SINK(object));
44 :
45 6 : if (IS_TOP_LEVEL_PROPERTY(prop_id)) {
46 5 : switch (prop_id) {
47 0 : case GPAC_PROP_SYNC:
48 0 : g_object_set_property(G_OBJECT(gpac_sink->sink), pspec->name, value);
49 0 : return;
50 5 : default:
51 5 : break;
52 : }
53 : }
54 :
55 : // Relay the property to the internal transform element
56 6 : g_object_set_property(G_OBJECT(gpac_sink->tf), pspec->name, value);
57 : }
58 :
59 : static void
60 0 : gst_gpac_sink_get_property(GObject* object,
61 : guint prop_id,
62 : GValue* value,
63 : GParamSpec* pspec)
64 : {
65 0 : GstGpacSink* gpac_sink = GST_GPAC_SINK(object);
66 0 : g_return_if_fail(GST_IS_GPAC_SINK(object));
67 :
68 0 : if (IS_TOP_LEVEL_PROPERTY(prop_id)) {
69 0 : switch (prop_id) {
70 0 : case GPAC_PROP_SYNC:
71 0 : g_object_get_property(G_OBJECT(gpac_sink->sink), pspec->name, value);
72 0 : return;
73 0 : default:
74 0 : break;
75 : }
76 : }
77 :
78 : // Relay the property to the internal transform element
79 0 : g_object_get_property(G_OBJECT(gpac_sink->tf), pspec->name, value);
80 : }
81 :
82 : // #MARK: Lifecycle
83 : static void
84 5 : gst_gpac_sink_reset(GstGpacSink* sink)
85 : {
86 : // Remove the internal elements
87 5 : if (sink->tf) {
88 0 : gst_bin_remove(GST_BIN(sink), sink->tf);
89 0 : sink->tf = NULL;
90 : }
91 :
92 5 : if (sink->sink) {
93 0 : gst_bin_remove(GST_BIN(sink), sink->sink);
94 0 : sink->sink = NULL;
95 : }
96 5 : }
97 :
98 : // #MARK: Pad Management
99 : static GstPad*
100 11 : gst_gpac_sink_request_new_pad(GstElement* element,
101 : GstPadTemplate* templ,
102 : const gchar* pad_name,
103 : const GstCaps* caps)
104 : {
105 11 : GstGpacSink* gpac_sink = GST_GPAC_SINK(element);
106 11 : GstPad* pad = NULL;
107 11 : GstPad* peer = NULL;
108 :
109 : // Request a new pad from the aggregator
110 11 : peer = gst_element_request_pad(gpac_sink->tf, templ, pad_name, caps);
111 11 : if (!peer) {
112 0 : GST_ELEMENT_ERROR(gpac_sink,
113 : STREAM,
114 : FAILED,
115 : (NULL),
116 : ("Unable to request pad name from the aggregator"));
117 0 : return NULL;
118 : }
119 :
120 : // Create a ghost pad from the aggregator
121 11 : pad = gst_ghost_pad_new_from_template(gst_pad_get_name(peer), peer, templ);
122 11 : gst_pad_set_active(pad, TRUE);
123 11 : gst_element_add_pad(element, pad);
124 11 : gst_object_unref(peer);
125 :
126 11 : return pad;
127 : }
128 :
129 : static void
130 0 : gst_gpac_sink_release_pad(GstElement* element, GstPad* pad)
131 : {
132 0 : GstGpacSink* gpac_sink = GST_GPAC_SINK(element);
133 0 : GstPad* peer = gst_pad_get_peer(pad);
134 :
135 0 : if (peer) {
136 0 : gst_element_release_request_pad(gpac_sink->tf, peer);
137 0 : gst_object_unref(peer);
138 : }
139 :
140 0 : gst_object_ref(pad);
141 0 : gst_element_remove_pad(element, pad);
142 0 : gst_pad_set_active(pad, FALSE);
143 0 : gst_object_unref(pad);
144 0 : }
145 :
146 : // #MARK: Initialization
147 : static void
148 5 : gst_gpac_sink_init(GstGpacSink* sink)
149 : {
150 5 : }
151 :
152 : static void
153 5 : gst_gpac_sink_instance_init(GstGpacSink* sink)
154 : {
155 5 : GstElement* element = GST_ELEMENT(sink);
156 5 : GObjectClass* klass = G_OBJECT_CLASS(G_TYPE_INSTANCE_GET_CLASS(
157 : G_OBJECT(element), GST_TYPE_GPAC_SINK, GstGpacSinkClass));
158 5 : GstGpacParams* params = GST_GPAC_GET_PARAMS(klass);
159 :
160 : // Reset the sink
161 5 : gst_gpac_sink_reset(sink);
162 :
163 : // Treat the bin as a sink
164 5 : GST_OBJECT_FLAG_SET(sink, GST_ELEMENT_FLAG_SINK);
165 :
166 : // Create the transform element for the subelement
167 5 : sink->tf = g_object_new(params->private_type, NULL);
168 :
169 : // Create a fake sink element
170 5 : sink->sink = gst_element_factory_make("fakesink", NULL);
171 :
172 : // Add and link the elements
173 5 : gst_bin_add_many(GST_BIN(sink), sink->tf, sink->sink, NULL);
174 5 : gst_element_link(sink->tf, sink->sink);
175 5 : }
176 :
177 : static void
178 2 : gst_gpac_sink_class_init(GstGpacSinkClass* klass)
179 : {
180 : // Initialize the class
181 2 : GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
182 2 : GstElementClass* gstelement_class = GST_ELEMENT_CLASS(klass);
183 2 : GstBinClass* gstbin_class = GST_BIN_CLASS(klass);
184 2 : parent_class = g_type_class_peek_parent(klass);
185 :
186 : // Install the pad templates
187 2 : gpac_install_sink_pad_templates(gstelement_class);
188 :
189 : // Set the pad management functions
190 2 : gstelement_class->request_new_pad =
191 2 : GST_DEBUG_FUNCPTR(gst_gpac_sink_request_new_pad);
192 2 : gstelement_class->release_pad = GST_DEBUG_FUNCPTR(gst_gpac_sink_release_pad);
193 2 : }
194 :
195 : // #MARK: Registration
196 : static void
197 4 : gst_gpac_sink_subclass_init(GstGpacSinkClass* klass)
198 : {
199 4 : GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
200 4 : GstElementClass* gstelement_class = GST_ELEMENT_CLASS(klass);
201 4 : GstGpacParams* params = GST_GPAC_GET_PARAMS(klass);
202 :
203 : // Set the property handlers
204 4 : gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_gpac_sink_set_property);
205 4 : gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_gpac_sink_get_property);
206 :
207 : // Set the property handlers
208 4 : gpac_install_global_properties(gobject_class);
209 4 : gpac_install_local_properties(
210 : gobject_class, GPAC_PROP_PRINT_STATS, GPAC_PROP_SYNC, GPAC_PROP_0);
211 :
212 : // Add the subclass-specific properties and pad templates
213 4 : if (params->is_single) {
214 : // Set property blacklist
215 2 : GList* blacklist = NULL;
216 2 : if (params->info->default_options) {
217 2 : filter_option* opt = params->info->default_options;
218 8 : for (u32 i = 0; opt[i].name; i++) {
219 6 : if (opt[i].forced)
220 4 : blacklist = g_list_append(blacklist, (gpointer)opt[i].name);
221 : }
222 : }
223 :
224 : // Install the filter properties
225 2 : gpac_install_filter_properties(
226 2 : gobject_class, blacklist, params->info->filter_name);
227 2 : g_list_free(blacklist);
228 :
229 : // Install the signals
230 2 : if (params->info->signal_presets) {
231 2 : gpac_install_signals_by_presets(gobject_class,
232 2 : params->info->signal_presets);
233 : }
234 :
235 : // Check if we have any filter options to expose
236 4 : for (u32 i = 0; i < G_N_ELEMENTS(filter_options); i++) {
237 2 : filter_option_overrides* opts = &filter_options[i];
238 2 : if (g_strcmp0(opts->filter_name, params->info->filter_name) == 0) {
239 0 : for (u32 j = 0; opts->options[j]; j++) {
240 0 : guint32 prop_id = opts->options[j];
241 0 : gpac_install_local_properties(gobject_class, prop_id, GPAC_PROP_0);
242 : }
243 0 : break;
244 : }
245 : }
246 :
247 : // Register the internal element type
248 2 : params->private_type =
249 2 : gst_gpac_tf_register_custom(params->info, TRUE, TRUE);
250 : } else {
251 2 : gpac_install_src_pad_templates(gstelement_class);
252 2 : gpac_install_local_properties(
253 : gobject_class, GPAC_PROP_GRAPH, GPAC_PROP_DESTINATION, GPAC_PROP_0);
254 2 : gpac_install_all_signals(gobject_class);
255 :
256 : // We need an internal version of the transform element
257 2 : params->info = g_new0(subelement_info, 1);
258 2 : const subelement_info internal_element =
259 : GPAC_TF_SUBELEMENT("internal", NULL, NULL);
260 2 : memcpy(params->info, &internal_element, sizeof(subelement_info));
261 :
262 : // Register the internal element type
263 2 : params->private_type =
264 2 : gst_gpac_tf_register_custom(params->info, TRUE, FALSE);
265 : }
266 :
267 : // Set the metadata
268 4 : const gchar* longname = "gpac sink";
269 4 : if (params->is_single) {
270 2 : if (!g_strcmp0(params->info->filter_name, params->info->alias_name))
271 0 : longname = g_strdup_printf("gpac %s sink", params->info->filter_name);
272 : else
273 2 : longname = g_strdup_printf("gpac %s (%s) sink",
274 2 : params->info->alias_name,
275 2 : params->info->filter_name);
276 : }
277 4 : gst_element_class_set_static_metadata(
278 : gstelement_class,
279 : longname,
280 : "Aggregator/Sink",
281 : "Uses gpac filter session aggregate and process the incoming data",
282 : "Deniz Ugur <deniz.ugur@motionspell.com>");
283 4 : }
284 :
285 : gboolean
286 2 : gst_gpac_sink_register(GstPlugin* plugin)
287 : {
288 : GType type;
289 : GstGpacParams* params;
290 2 : GTypeInfo subclass_typeinfo = {
291 : sizeof(GstGpacSinkClass),
292 : NULL, // base_init
293 : NULL, // base_finalize
294 : (GClassInitFunc)gst_gpac_sink_subclass_init,
295 : NULL, // class_finalize
296 : NULL, // class_data
297 : sizeof(GstGpacSink),
298 : 0,
299 : (GInstanceInitFunc)gst_gpac_sink_instance_init,
300 : };
301 :
302 2 : GST_DEBUG_CATEGORY_INIT(gst_gpac_sink_debug, "gpacsink", 0, "GPAC Sink");
303 :
304 : // Register the regular sink element
305 2 : GST_LOG("Registering regular gpac sink element");
306 2 : params = g_new0(GstGpacParams, 1);
307 2 : params->is_single = FALSE;
308 2 : type = g_type_register_static(
309 : GST_TYPE_GPAC_SINK, "GstGpacSinkRegular", &subclass_typeinfo, 0);
310 2 : g_type_set_qdata(type, GST_GPAC_PARAMS_QDATA, params);
311 2 : if (!gst_element_register(plugin, "gpacsink", GST_RANK_PRIMARY, type)) {
312 0 : GST_ERROR_OBJECT(plugin, "Failed to register regular gpac sink element");
313 0 : return FALSE;
314 : }
315 :
316 : // Register subelements
317 10 : for (u32 i = 0; i < G_N_ELEMENTS(subelements); i++) {
318 8 : subelement_info* info = &subelements[i];
319 :
320 8 : if (!(info->flags & GPAC_SE_SINK_ONLY)) {
321 6 : GST_DEBUG_OBJECT(plugin,
322 : "Subelement %s is not a sink only element, skipping",
323 : info->alias_name);
324 6 : continue;
325 : }
326 :
327 : // Register the regular sink element
328 2 : GST_LOG("Registering %s sink subelement", info->filter_name);
329 2 : params = g_new0(GstGpacParams, 1);
330 2 : params->is_single = TRUE;
331 2 : params->info = info;
332 2 : const gchar* name = g_strdup_printf("gpac%ssink", info->alias_name);
333 : const gchar* type_name =
334 2 : g_strdup_printf("GstGpacSink%c%s",
335 2 : g_ascii_toupper(info->alias_name[0]),
336 2 : info->alias_name + 1);
337 :
338 2 : type = g_type_register_static(
339 : GST_TYPE_GPAC_SINK, type_name, &subclass_typeinfo, 0);
340 2 : g_type_set_qdata(type, GST_GPAC_PARAMS_QDATA, params);
341 2 : if (!gst_element_register(plugin, name, GST_RANK_SECONDARY, type)) {
342 0 : GST_ERROR_OBJECT(
343 : plugin, "Failed to register %s sink subelement", info->alias_name);
344 0 : return FALSE;
345 : }
346 : }
347 :
348 2 : return TRUE;
349 : }
350 :
351 2 : GST_ELEMENT_REGISTER_DEFINE_CUSTOM(gpac_sink, gst_gpac_sink_register);
|