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_sys_init(GF_MemTrackerNone, NULL);
158 8 : GF_FilterSession* session = gf_fs_new_defaults(0U);
159 8 : guint num_filters = gf_fs_filters_registers_count(session);
160 :
161 : // Get the list seperator
162 : GF_Err e;
163 8 : GF_Filter* dummy = gf_fs_new_filter(session, "dummy", 0, &e);
164 8 : char sep_list = (char)gf_filter_get_sep(dummy, GF_FS_SEP_LIST);
165 :
166 : const GF_FilterRegister* filter;
167 459 : for (guint i = 0; i < num_filters; i++) {
168 459 : filter = gf_fs_get_filter_register(session, i);
169 459 : if (!g_strcmp0(filter->name, filter_name))
170 8 : break;
171 451 : filter = NULL;
172 : }
173 8 : gf_fs_del(session);
174 8 : gf_sys_close();
175 8 : g_assert(filter);
176 :
177 8 : guint option_idx = 0;
178 619 : while (filter->args && filter->args[option_idx].arg_name) {
179 : // Skip hidden options
180 611 : if (filter->args[option_idx].flags & GF_ARG_HINT_HIDE)
181 25 : goto skip;
182 :
183 : // Check if the option name is valid
184 586 : if (!g_param_spec_is_valid_name(filter->args[option_idx].arg_name))
185 0 : goto skip;
186 :
187 : // Check if the option is already registered
188 586 : if (g_object_class_find_property(gobject_class,
189 586 : filter->args[option_idx].arg_name))
190 1 : goto skip;
191 :
192 : // Check if the option is blacklisted
193 : GList* item;
194 1248 : for (item = blacklist; item; item = item->next)
195 671 : if (!g_strcmp0(filter->args[option_idx].arg_name, item->data))
196 8 : goto skip;
197 :
198 : #define SPEC_INSTALL(type, ...) \
199 : g_object_class_install_property( \
200 : gobject_class, \
201 : GPAC_PROP_FILTER_OFFSET + option_idx, \
202 : g_param_spec_##type(filter->args[option_idx].arg_name, \
203 : filter->args[option_idx].arg_name, \
204 : filter->args[option_idx].arg_desc, \
205 : __VA_ARGS__, \
206 : G_PARAM_WRITABLE));
207 :
208 : // Special case for enum values
209 577 : if (filter->args[option_idx].min_max_enum &&
210 110 : strstr(filter->args[option_idx].min_max_enum, "|")) {
211 110 : SPEC_INSTALL(string, NULL);
212 110 : goto skip;
213 : }
214 :
215 : // Parse the default value
216 : const GF_PropertyValue p =
217 467 : gf_props_parse_value(filter->args[option_idx].arg_type,
218 467 : filter->args[option_idx].arg_name,
219 467 : filter->args[option_idx].arg_default_val,
220 467 : filter->args[option_idx].min_max_enum,
221 : sep_list);
222 :
223 : // Register the option
224 467 : switch (filter->args[option_idx].arg_type) {
225 25 : case GF_PROP_SINT:
226 25 : SPEC_INSTALL(int, G_MININT, G_MAXINT, p.value.sint);
227 25 : break;
228 45 : case GF_PROP_UINT:
229 45 : SPEC_INSTALL(uint, 0, G_MAXUINT, (guint)p.value.uint);
230 45 : break;
231 1 : case GF_PROP_LSINT:
232 1 : SPEC_INSTALL(int64, G_MININT64, G_MAXINT64, (gint64)p.value.longsint);
233 1 : break;
234 2 : case GF_PROP_LUINT:
235 2 : SPEC_INSTALL(uint64, 0, G_MAXUINT64, (guint64)p.value.longuint);
236 2 : break;
237 11 : case GF_PROP_FLOAT:
238 : case GF_PROP_FRACTION: {
239 11 : gfloat fval = 0.0F;
240 11 : if (p.value.lfrac.den != 0)
241 0 : fval = (gfloat)p.value.frac.num / (gfloat)p.value.frac.den;
242 11 : SPEC_INSTALL(float, -G_MAXFLOAT, G_MAXFLOAT, fval);
243 11 : break;
244 : }
245 19 : case GF_PROP_DOUBLE:
246 : case GF_PROP_FRACTION64: {
247 19 : gdouble dval = 0.0;
248 19 : if (p.value.lfrac.den != 0)
249 4 : dval = (gdouble)p.value.lfrac.num / (gdouble)p.value.lfrac.den;
250 19 : SPEC_INSTALL(double, -G_MAXDOUBLE, G_MAXDOUBLE, dval);
251 19 : break;
252 : }
253 284 : case GF_PROP_BOOL:
254 284 : SPEC_INSTALL(boolean, p.value.boolean);
255 284 : break;
256 71 : case GF_PROP_STRING:
257 71 : SPEC_INSTALL(string, p.value.string);
258 71 : break;
259 9 : default:
260 9 : SPEC_INSTALL(string, NULL);
261 9 : break;
262 : }
263 :
264 : #undef SPEC_INSTALL
265 :
266 611 : skip:
267 611 : option_idx++;
268 : }
269 8 : }
270 :
271 : void
272 12 : gpac_install_global_properties(GObjectClass* gobject_class)
273 : {
274 : // Install the visible global properties
275 12 : const GF_GPACArg* args = gf_sys_get_options();
276 12 : for (u32 i = GPAC_PROP_GLOBAL_OFFSET;
277 60 : visible_properties[i - GPAC_PROP_GLOBAL_OFFSET].name;
278 48 : i++) {
279 : // Find the property in the global properties
280 1224 : for (u32 j = 0; args[j].name; j++) {
281 1224 : const GF_GPACArg* arg = &args[j];
282 1224 : const GPAC_Arg* prop = &visible_properties[i - GPAC_PROP_GLOBAL_OFFSET];
283 1224 : if (!g_strcmp0(prop->name, arg->name)) {
284 48 : g_object_class_install_property(
285 : gobject_class,
286 : i,
287 48 : prop->is_simple ? g_param_spec_boolean(arg->name,
288 48 : arg->name,
289 48 : arg->description,
290 : FALSE,
291 : G_PARAM_READWRITE)
292 0 : : g_param_spec_string(arg->name,
293 0 : arg->name,
294 0 : arg->description,
295 : NULL,
296 : G_PARAM_READWRITE));
297 48 : 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 is simple
361 : gboolean is_simple, is_visible;
362 0 : gpac_get_property_attributes(pspec, &is_simple, &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 (is_simple)
371 0 : property = g_strdup_printf("-%s", g_param_spec_get_name(pspec));
372 : else
373 0 : property = g_strdup_printf(
374 : "--%s=%s", g_param_spec_get_name(pspec), g_value_get_string(value));
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 is_simple, is_visible;
408 0 : gpac_get_property_attributes(pspec, &is_simple, &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 (is_simple)
414 0 : g_value_set_boolean(value, prop_val != NULL);
415 : else
416 0 : g_value_set_string(value, prop_val);
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 : }
|