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