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 "lib/properties.h"
27 : #include "gpacmessages.h"
28 : #include <gpac/filters.h>
29 :
30 : typedef struct
31 : {
32 : const gchar* name;
33 : gboolean is_simple; // if true, treat as "-arg" instead of "--arg=..."
34 : } GPAC_Arg;
35 : #define GPAC_DEF_ARG(name, is_simple) { name, is_simple }
36 :
37 : // Define the override properties
38 : static GF_GPACArg override_properties[] = {
39 : { 0 },
40 : };
41 :
42 : // Define the visible properties with their default values
43 : static GPAC_Arg visible_properties[] = {
44 : GPAC_DEF_ARG("for-test", TRUE),
45 : GPAC_DEF_ARG("old-arch", TRUE),
46 : GPAC_DEF_ARG("strict-error", TRUE),
47 : GPAC_DEF_ARG("broken-cert", TRUE),
48 : { 0 },
49 : };
50 :
51 : void
52 0 : gpac_get_property_attributes(GParamSpec* pspec,
53 : gboolean* is_simple,
54 : gboolean* is_visible)
55 : {
56 0 : *is_visible = FALSE;
57 0 : *is_simple = FALSE;
58 0 : for (u32 i = 0; visible_properties[i].name; i++) {
59 0 : if (!g_strcmp0(g_param_spec_get_name(pspec), visible_properties[i].name)) {
60 0 : *is_simple = visible_properties[i].is_simple;
61 0 : *is_visible = TRUE;
62 0 : break;
63 : }
64 : }
65 0 : }
66 :
67 : void
68 22 : gpac_install_local_properties(GObjectClass* gobject_class,
69 : GPAC_PropertyId first_prop,
70 : ...)
71 : {
72 : // Initialize the variable argument list
73 : va_list args;
74 22 : GPAC_PropertyId prop = first_prop;
75 22 : va_start(args, first_prop);
76 :
77 52 : while (prop != GPAC_PROP_0) {
78 30 : switch (prop) {
79 4 : case GPAC_PROP_GRAPH:
80 4 : g_object_class_install_property(
81 : gobject_class,
82 : prop,
83 : g_param_spec_string("graph",
84 : "Graph",
85 : "Filter graph to use in gpac session",
86 : NULL,
87 : G_PARAM_READWRITE));
88 4 : break;
89 :
90 12 : case GPAC_PROP_PRINT_STATS:
91 12 : g_object_class_install_property(
92 : gobject_class,
93 : prop,
94 : g_param_spec_boolean("print-stats",
95 : "Print Stats",
96 : "Print filter session stats after execution",
97 : FALSE,
98 : G_PARAM_READWRITE));
99 12 : break;
100 :
101 4 : case GPAC_PROP_SYNC:
102 4 : g_object_class_install_property(
103 : gobject_class,
104 : prop,
105 : g_param_spec_boolean(
106 : "sync",
107 : "Sync",
108 : "Enable synchronization on the internal fakesink",
109 : FALSE,
110 : G_PARAM_READWRITE));
111 4 : break;
112 :
113 4 : case GPAC_PROP_DESTINATION:
114 4 : g_object_class_install_property(
115 : gobject_class,
116 : prop,
117 : g_param_spec_string("destination",
118 : "Destination",
119 : "Destination to use for the filter session. Adds "
120 : "a new sink to the graph",
121 : NULL,
122 : G_PARAM_READWRITE));
123 4 : break;
124 :
125 6 : case GPAC_PROP_SEGDUR:
126 6 : g_object_class_install_property(
127 : gobject_class,
128 : prop,
129 : g_param_spec_float(
130 : "segdur",
131 : "Segment Duration",
132 : "Duration of each segment in seconds. This option is handled from "
133 : "the GStreamer side and not relayed to gpac",
134 : 0,
135 : G_MAXFLOAT,
136 : 0,
137 : G_PARAM_READWRITE));
138 6 : break;
139 :
140 0 : default:
141 0 : break;
142 : }
143 :
144 : // Get the next property
145 30 : prop = va_arg(args, GPAC_PropertyId);
146 : }
147 :
148 : // End the variable argument list
149 22 : va_end(args);
150 22 : }
151 :
152 : void
153 8 : gpac_install_filter_properties(GObjectClass* gobject_class,
154 : GList* blacklist,
155 : const gchar* filter_name)
156 : {
157 8 : GF_FilterSession* session = gf_fs_new_defaults(0U);
158 8 : guint num_filters = gf_fs_filters_registers_count(session);
159 :
160 : // Get the list seperator
161 : GF_Err e;
162 8 : GF_Filter* dummy = gf_fs_new_filter(session, "dummy", 0, &e);
163 8 : char sep_list = (char)gf_filter_get_sep(dummy, GF_FS_SEP_LIST);
164 :
165 : const GF_FilterRegister* filter;
166 459 : for (guint i = 0; i < num_filters; i++) {
167 459 : filter = gf_fs_get_filter_register(session, i);
168 459 : if (!g_strcmp0(filter->name, filter_name))
169 8 : break;
170 451 : filter = NULL;
171 : }
172 8 : gf_fs_del(session);
173 8 : g_assert(filter);
174 :
175 8 : guint option_idx = 0;
176 606 : while (filter->args && filter->args[option_idx].arg_name) {
177 : // Skip hidden options
178 598 : if (filter->args[option_idx].flags & GF_ARG_HINT_HIDE)
179 25 : goto skip;
180 :
181 : // Check if the option name is valid
182 573 : if (!g_param_spec_is_valid_name(filter->args[option_idx].arg_name))
183 0 : goto skip;
184 :
185 : // Check if the option is already registered
186 573 : if (g_object_class_find_property(gobject_class,
187 573 : filter->args[option_idx].arg_name))
188 1 : goto skip;
189 :
190 : // Check if the option is blacklisted
191 : GList* item;
192 1215 : for (item = blacklist; item; item = item->next)
193 651 : if (!g_strcmp0(filter->args[option_idx].arg_name, item->data))
194 8 : goto skip;
195 :
196 : #define SPEC_INSTALL(type, ...) \
197 : g_object_class_install_property( \
198 : gobject_class, \
199 : GPAC_PROP_FILTER_OFFSET + option_idx, \
200 : g_param_spec_##type(filter->args[option_idx].arg_name, \
201 : filter->args[option_idx].arg_name, \
202 : filter->args[option_idx].arg_desc, \
203 : __VA_ARGS__, \
204 : G_PARAM_WRITABLE));
205 :
206 : // Special case for enum values
207 564 : if (filter->args[option_idx].min_max_enum &&
208 106 : strstr(filter->args[option_idx].min_max_enum, "|")) {
209 106 : SPEC_INSTALL(string, NULL);
210 106 : goto skip;
211 : }
212 :
213 : // Parse the default value
214 : const GF_PropertyValue p =
215 458 : gf_props_parse_value(filter->args[option_idx].arg_type,
216 458 : filter->args[option_idx].arg_name,
217 458 : filter->args[option_idx].arg_default_val,
218 458 : filter->args[option_idx].min_max_enum,
219 : sep_list);
220 :
221 : // Register the option
222 458 : switch (filter->args[option_idx].arg_type) {
223 25 : case GF_PROP_SINT:
224 25 : SPEC_INSTALL(int, G_MININT, G_MAXINT, p.value.sint);
225 25 : break;
226 41 : case GF_PROP_UINT:
227 41 : SPEC_INSTALL(uint, 0, G_MAXUINT, (guint)p.value.uint);
228 41 : break;
229 1 : case GF_PROP_LSINT:
230 1 : SPEC_INSTALL(int64, G_MININT64, G_MAXINT64, (gint64)p.value.longsint);
231 1 : break;
232 2 : case GF_PROP_LUINT:
233 2 : SPEC_INSTALL(uint64, 0, G_MAXUINT64, (guint64)p.value.longuint);
234 2 : break;
235 11 : case GF_PROP_FLOAT:
236 : case GF_PROP_FRACTION: {
237 11 : gfloat fval = 0.0F;
238 11 : if (p.value.lfrac.den != 0)
239 0 : fval = (gfloat)p.value.frac.num / (gfloat)p.value.frac.den;
240 11 : SPEC_INSTALL(float, -G_MAXFLOAT, G_MAXFLOAT, fval);
241 11 : break;
242 : }
243 19 : case GF_PROP_DOUBLE:
244 : case GF_PROP_FRACTION64: {
245 19 : gdouble dval = 0.0;
246 19 : if (p.value.lfrac.den != 0)
247 4 : dval = (gdouble)p.value.lfrac.num / (gdouble)p.value.lfrac.den;
248 19 : SPEC_INSTALL(double, -G_MAXDOUBLE, G_MAXDOUBLE, dval);
249 19 : break;
250 : }
251 282 : case GF_PROP_BOOL:
252 282 : SPEC_INSTALL(boolean, p.value.boolean);
253 282 : break;
254 68 : case GF_PROP_STRING:
255 68 : SPEC_INSTALL(string, p.value.string);
256 68 : break;
257 9 : default:
258 9 : SPEC_INSTALL(string, NULL);
259 9 : break;
260 : }
261 :
262 : #undef SPEC_INSTALL
263 :
264 598 : skip:
265 598 : option_idx++;
266 : }
267 8 : }
268 :
269 : void
270 12 : gpac_install_global_properties(GObjectClass* gobject_class)
271 : {
272 : // Install the visible global properties
273 12 : const GF_GPACArg* args = gf_sys_get_options();
274 12 : for (u32 i = GPAC_PROP_GLOBAL_OFFSET;
275 60 : visible_properties[i - GPAC_PROP_GLOBAL_OFFSET].name;
276 48 : i++) {
277 : // Find the property in the global properties
278 1224 : for (u32 j = 0; args[j].name; j++) {
279 1224 : const GF_GPACArg* arg = &args[j];
280 1224 : const GPAC_Arg* prop = &visible_properties[i - GPAC_PROP_GLOBAL_OFFSET];
281 1224 : if (!g_strcmp0(prop->name, arg->name)) {
282 48 : g_object_class_install_property(
283 : gobject_class,
284 : i,
285 48 : prop->is_simple ? g_param_spec_boolean(arg->name,
286 48 : arg->name,
287 48 : arg->description,
288 : FALSE,
289 : G_PARAM_READWRITE)
290 0 : : g_param_spec_string(arg->name,
291 0 : arg->name,
292 0 : arg->description,
293 : NULL,
294 : G_PARAM_READWRITE));
295 48 : break;
296 : }
297 : }
298 : }
299 12 : }
300 :
301 : gboolean
302 14 : gpac_set_property(GPAC_PropertyContext* ctx,
303 : guint property_id,
304 : const GValue* value,
305 : GParamSpec* pspec)
306 : {
307 14 : if (g_param_value_defaults(pspec, value))
308 0 : return TRUE;
309 :
310 14 : if (IS_TOP_LEVEL_PROPERTY(property_id)) {
311 5 : switch (property_id) {
312 4 : case GPAC_PROP_GRAPH:
313 4 : g_free(ctx->graph);
314 4 : ctx->graph = g_value_dup_string(value);
315 4 : break;
316 0 : case GPAC_PROP_PRINT_STATS:
317 0 : ctx->print_stats = g_value_get_boolean(value);
318 0 : break;
319 0 : case GPAC_PROP_SYNC:
320 0 : ctx->sync = g_value_get_boolean(value);
321 0 : break;
322 1 : case GPAC_PROP_DESTINATION:
323 1 : g_free(ctx->destination);
324 1 : ctx->destination = g_value_dup_string(value);
325 1 : break;
326 0 : default:
327 0 : return FALSE;
328 : }
329 9 : } else if (IS_FILTER_PROPERTY(property_id)) {
330 : // Get the value as a string
331 : gchar* value_str;
332 7 : if (G_VALUE_HOLDS_STRING(value))
333 0 : value_str = g_value_dup_string(value);
334 7 : else if (G_VALUE_HOLDS_BOOLEAN(value))
335 0 : value_str = g_strdup(g_value_get_boolean(value) ? "true" : "false");
336 : else
337 7 : value_str = g_strdup_value_contents(value);
338 :
339 : // No value, return
340 7 : if (!value_str)
341 0 : return TRUE;
342 :
343 : // Convert snake case to kebab case
344 : // GLib uses kebab case for command line arguments, so we have to convert it
345 : // back to snake case
346 7 : gchar* param = g_strdup(g_param_spec_get_name(pspec));
347 37 : for (gchar* c = param; *c; c++)
348 30 : if (*c == '-')
349 0 : *c = '_';
350 :
351 : // Add the property to the list
352 7 : gchar* property = g_strdup_printf("--%s=%s", param, value_str);
353 7 : ctx->properties = g_list_append(ctx->properties, property);
354 7 : g_free(value_str);
355 7 : g_free(param);
356 7 : return TRUE;
357 2 : } else if (IS_GLOBAL_PROPERTY(property_id)) {
358 : // Check if the property is visible and if it is simple
359 : gboolean is_simple, is_visible;
360 0 : gpac_get_property_attributes(pspec, &is_simple, &is_visible);
361 :
362 : // If the property is not visible, return
363 0 : if (!is_visible)
364 0 : return FALSE;
365 :
366 : // Add the property to the list
367 : gchar* property;
368 0 : if (is_simple)
369 0 : property = g_strdup_printf("-%s", g_param_spec_get_name(pspec));
370 : else
371 0 : property = g_strdup_printf(
372 : "--%s=%s", g_param_spec_get_name(pspec), g_value_get_string(value));
373 0 : ctx->properties = g_list_append(ctx->properties, property);
374 : } else {
375 : // Unknown property or not handled here
376 2 : return FALSE;
377 : }
378 5 : return TRUE;
379 : }
380 :
381 : gboolean
382 0 : gpac_get_property(GPAC_PropertyContext* ctx,
383 : guint property_id,
384 : GValue* value,
385 : GParamSpec* pspec)
386 : {
387 0 : if (IS_TOP_LEVEL_PROPERTY(property_id)) {
388 0 : switch (property_id) {
389 0 : case GPAC_PROP_GRAPH:
390 0 : g_value_set_string(value, ctx->graph);
391 0 : break;
392 0 : case GPAC_PROP_PRINT_STATS:
393 0 : g_value_set_boolean(value, ctx->print_stats);
394 0 : break;
395 0 : case GPAC_PROP_SYNC:
396 0 : g_value_set_boolean(value, ctx->sync);
397 0 : break;
398 0 : case GPAC_PROP_DESTINATION:
399 0 : g_value_set_string(value, ctx->destination);
400 0 : break;
401 0 : default:
402 0 : return FALSE;
403 : }
404 0 : } else if (IS_GLOBAL_PROPERTY(property_id)) {
405 : gboolean is_simple, is_visible;
406 0 : gpac_get_property_attributes(pspec, &is_simple, &is_visible);
407 0 : g_assert(is_visible);
408 : const gchar* prop_val =
409 0 : gf_sys_find_global_arg(g_param_spec_get_name(pspec));
410 :
411 0 : if (is_simple)
412 0 : g_value_set_boolean(value, prop_val != NULL);
413 : else
414 0 : g_value_set_string(value, prop_val);
415 0 : } else if (IS_ELEMENT_PROPERTY(property_id)) {
416 : // Handled in the element
417 0 : return FALSE;
418 : } else {
419 : // Unknown property or not handled here
420 0 : g_warn_if_reached();
421 : }
422 0 : return TRUE;
423 : }
424 :
425 : gboolean
426 16 : gpac_apply_properties(GPAC_PropertyContext* ctx)
427 : {
428 : // Add the default properties
429 16 : g_autolist(GList) override_list = NULL;
430 16 : for (u32 i = 0; override_properties[i].name; i++) {
431 0 : const GF_GPACArg* arg = &override_properties[i];
432 0 : gchar* property = g_strdup_printf("--%s=%s", arg->name, arg->val);
433 0 : override_list = g_list_append(override_list, property);
434 : }
435 :
436 : // Free the previous arguments
437 16 : if (ctx->props_as_argv) {
438 9 : for (u32 i = 0; ctx->props_as_argv[i]; i++)
439 6 : g_free(ctx->props_as_argv[i]);
440 3 : g_free((void*)ctx->props_as_argv);
441 3 : ctx->props_as_argv = NULL;
442 : }
443 :
444 : // Allocate the arguments array
445 16 : u32 num_properties =
446 16 : g_list_length(ctx->properties) + g_list_length(override_list);
447 : // +1 for the executable name
448 16 : num_properties++;
449 : // +1 for the NULL termination
450 16 : num_properties++;
451 :
452 16 : ctx->props_as_argv = (gchar**)g_malloc0_n(num_properties, sizeof(gchar*));
453 :
454 : // Add the executable name
455 16 : ctx->props_as_argv[0] = g_strdup("gst");
456 :
457 : void* item;
458 26 : for (u32 i = 0; i < g_list_length(ctx->properties); i++) {
459 10 : item = g_list_nth_data(ctx->properties, i);
460 20 : ctx->props_as_argv[i + 1] = (gchar*)g_strdup(item);
461 : }
462 16 : for (u32 i = 0; i < g_list_length(override_list); i++) {
463 0 : item = g_list_nth_data(override_list, i);
464 0 : ctx->props_as_argv[i + 1 + g_list_length(ctx->properties)] = (gchar*)item;
465 : }
466 :
467 : // Add the NULL termination
468 16 : ctx->props_as_argv[num_properties - 1] = NULL;
469 :
470 : // Set the gpac system arguments
471 16 : gpac_return_val_if_fail(
472 : gf_sys_set_args((s32)num_properties - 1, (const char**)ctx->props_as_argv),
473 : FALSE);
474 :
475 16 : return TRUE;
476 : }
|