Update for g_type_class_add_private() deprecation in recent GLib
[platform/upstream/gstreamer.git] / ges / ges-command-line-formatter.c
1 /* GStreamer Editing Services
2  *
3  * Copyright (C) <2015> Thibault Saunier <tsaunier@gnome.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "ges-command-line-formatter.h"
22
23 #include "ges/ges-structured-interface.h"
24 #include "ges-structure-parser.h"
25 #include "ges-internal.h"
26 #define YY_NO_UNISTD_H
27 #include "ges-parse-lex.h"
28
29 struct _GESCommandLineFormatterPrivate
30 {
31   gpointer dummy;
32 };
33
34
35 G_DEFINE_TYPE_WITH_PRIVATE (GESCommandLineFormatter, ges_command_line_formatter,
36     GES_TYPE_FORMATTER);
37
38 static gboolean
39 _ges_command_line_formatter_add_clip (GESTimeline * timeline,
40     GstStructure * structure, GError ** error);
41 static gboolean
42 _ges_command_line_formatter_add_effect (GESTimeline * timeline,
43     GstStructure * structure, GError ** error);
44 static gboolean
45 _ges_command_line_formatter_add_test_clip (GESTimeline * timeline,
46     GstStructure * structure, GError ** error);
47 static gboolean
48 _ges_command_line_formatter_add_title_clip (GESTimeline * timeline,
49     GstStructure * structure, GError ** error);
50
51 typedef struct
52 {
53   const gchar *long_name;
54   const gchar *short_name;
55   GType type;
56   const gchar *new_name;
57   const gchar *desc;
58 } Property;
59
60 // Currently Clip has the most properties.. adapt as needed
61 #define MAX_PROPERTIES 8
62 typedef struct
63 {
64   const gchar *long_name;
65   gchar short_name;
66   ActionFromStructureFunc callback;
67   const gchar *description;
68   /* The first property must be the ID on the command line */
69   Property properties[MAX_PROPERTIES];
70 } GESCommandLineOption;
71
72 /*  *INDENT-OFF* */
73 static GESCommandLineOption options[] = {
74   {"clip", 'c', (ActionFromStructureFunc) _ges_command_line_formatter_add_clip,
75     "<clip uri> - Adds a clip in the timeline.",
76     {
77       {
78         "uri", "n", 0, "asset-id",
79         "The URI of the media file."
80       },
81       {
82         "name", "n", 0, NULL,
83         "The name of the clip, can be used as an ID later."
84       },
85       {
86         "start", "s",GST_TYPE_CLOCK_TIME, NULL,
87         "The starting position of the clip in the timeline."
88       },
89       {
90         "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
91         "The duration of the clip."
92       },
93       {
94         "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
95         "The inpoint of the clip (time in the input file to start playing from)."
96       },
97       {
98         "track-types", "tt", 0, NULL,
99         "The type of the tracks where the clip should be used (audio or video or audio+video)."
100       },
101       {
102         "layer", "l", 0, NULL,
103         "The priority of the layer into which the clip should be added."
104       },
105       {NULL, 0, 0, NULL, FALSE},
106     },
107   },
108   {"effect", 'e', (ActionFromStructureFunc) _ges_command_line_formatter_add_effect,
109     "<effect bin description> - Adds an effect as specified by 'bin-description',\n"
110     "similar to gst-launch-style pipeline description, without setting properties\n"
111     "(see `set-` for information about how to set properties).\n",
112     {
113       {
114         "bin-description", "d", 0, "asset-id",
115         "gst-launch style bin description."
116       },
117       {
118         "element-name", "e", 0, NULL,
119         "The name of the element to apply the effect on."
120       },
121       {
122         "name", "n", 0, "child-name",
123         "The name to be given to the effect."
124       },
125       {NULL, NULL, 0, NULL, FALSE},
126     },
127   },
128   {"test-clip", 0, (ActionFromStructureFunc) _ges_command_line_formatter_add_test_clip,
129     "<test clip pattern> - Add a test clip in the timeline.",
130     {
131       {
132         "pattern", "p", 0, NULL,
133         "The testsource pattern name."
134       },
135       {
136         "name", "n", 0, NULL,
137         "The name of the clip, can be used as an ID later."
138       },
139       {
140         "start", "s",GST_TYPE_CLOCK_TIME, NULL,
141         "The starting position of the clip in the timeline."
142       },
143       {
144         "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
145         "The duration of the clip."
146       },
147       {
148         "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
149         "The inpoint of the clip (time in the input file to start playing)."
150       },
151       {
152         "layer", "l", 0, NULL,
153         "The priority of the layer into which the clip should be added."
154       },
155       {NULL, 0, 0, NULL, FALSE},
156     },
157   },
158   {"title", 'c', (ActionFromStructureFunc) _ges_command_line_formatter_add_title_clip,
159     "<title text> - Adds a clip in the timeline.",
160     {
161       {
162         "text", "n", 0, NULL,
163         "The text to be used as title."
164       },
165       {
166         "name", "n", 0, NULL,
167         "The name of the clip, can be used as an ID later."
168       },
169       {
170         "start", "s",GST_TYPE_CLOCK_TIME, NULL,
171         "The starting position of the clip in the timeline."
172       },
173       {
174         "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
175         "The duration of the clip."
176       },
177       {
178         "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
179         "The inpoint of the clip (time in the input file to start playing from)."
180       },
181       {
182         "track-types", "tt", 0, NULL,
183         "The type of the tracks where the clip should be used (audio or video or audio+video)."
184       },
185       {
186         "layer", "l", 0, NULL,
187         "The priority of the layer into which the clip should be added."
188       },
189       {NULL, 0, 0, NULL, FALSE},
190     },
191   },
192   {
193     "set-", 0, NULL,
194     "<property name> <value> - Set a property on the last added element.\n"
195     "Any child property that exists on the previously added element\n"
196     "can be used as <property name>",
197     {
198       {NULL, NULL, 0, NULL, FALSE},
199     },
200   },
201 };
202 /*  *INDENT-ON* */
203
204 /* Should always be in the same order as the options */
205 typedef enum
206 {
207   CLIP,
208   EFFECT,
209   TEST_CLIP,
210   TITLE,
211   SET,
212 } GESCommandLineOptionType;
213
214 static gint                     /*  -1: not present, 0: failure, 1: OK */
215 _convert_to_clocktime (GstStructure * structure, const gchar * name,
216     GstClockTime default_value)
217 {
218   gint res = 1;
219   gdouble val;
220   GValue d_val = { 0 };
221   GstClockTime timestamp;
222   const GValue *gvalue = gst_structure_get_value (structure, name);
223
224   if (gvalue == NULL) {
225     timestamp = default_value;
226
227     res = -1;
228
229     goto done;
230   }
231
232   if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME)
233     return 1;
234
235   g_value_init (&d_val, G_TYPE_DOUBLE);
236   if (!g_value_transform (gvalue, &d_val)) {
237     GST_ERROR ("Could not get timestamp for %s", name);
238
239     return 0;
240   }
241   val = g_value_get_double ((const GValue *) &d_val);
242
243   if (val == -1.0)
244     timestamp = GST_CLOCK_TIME_NONE;
245   else
246     timestamp = val * GST_SECOND;
247
248 done:
249   gst_structure_set (structure, name, G_TYPE_UINT64, timestamp, NULL);
250
251   return res;
252 }
253
254 static gboolean
255 _cleanup_fields (const Property * field_names, GstStructure * structure,
256     GError ** error)
257 {
258   guint i;
259
260   for (i = 0; field_names[i].long_name; i++) {
261     gboolean exists = FALSE;
262
263     /* Move shortly named fields to longname variante */
264     if (gst_structure_has_field (structure, field_names[i].short_name)) {
265       exists = TRUE;
266
267       if (gst_structure_has_field (structure, field_names[i].long_name)) {
268         *error = g_error_new (GES_ERROR, 0, "Using short and long name"
269             " at the same time for property: %s, which one should I use?!",
270             field_names[i].long_name);
271
272         return FALSE;
273       } else {
274         const GValue *val =
275             gst_structure_get_value (structure, field_names[i].short_name);
276
277         gst_structure_set_value (structure, field_names[i].long_name, val);
278         gst_structure_remove_field (structure, field_names[i].short_name);
279       }
280     } else if (gst_structure_has_field (structure, field_names[i].long_name)) {
281       exists = TRUE;
282     }
283
284     if (exists) {
285       if (field_names[i].type == GST_TYPE_CLOCK_TIME) {
286         if (_convert_to_clocktime (structure, field_names[i].long_name, 0) == 0) {
287           *error = g_error_new (GES_ERROR, 0, "Could not convert"
288               " %s to GstClockTime", field_names[i].long_name);
289
290           return FALSE;
291         }
292       }
293     }
294
295     if (field_names[i].new_name
296         && gst_structure_has_field (structure, field_names[i].long_name)) {
297       const GValue *val =
298           gst_structure_get_value (structure, field_names[i].long_name);
299
300       gst_structure_set_value (structure, field_names[i].new_name, val);
301       gst_structure_remove_field (structure, field_names[i].long_name);
302     }
303   }
304
305   return TRUE;
306 }
307
308 static gboolean
309 _ges_command_line_formatter_add_clip (GESTimeline * timeline,
310     GstStructure * structure, GError ** error)
311 {
312   if (!_cleanup_fields (options[CLIP].properties, structure, error))
313     return FALSE;
314
315   gst_structure_set (structure, "type", G_TYPE_STRING, "GESUriClip", NULL);
316
317   return _ges_add_clip_from_struct (timeline, structure, error);
318 }
319
320 static gboolean
321 _ges_command_line_formatter_add_test_clip (GESTimeline * timeline,
322     GstStructure * structure, GError ** error)
323 {
324   if (!_cleanup_fields (options[TEST_CLIP].properties, structure, error))
325     return FALSE;
326
327   gst_structure_set (structure, "type", G_TYPE_STRING, "GESTestClip", NULL);
328   gst_structure_set (structure, "asset-id", G_TYPE_STRING,
329       gst_structure_get_string (structure, "pattern"), NULL);
330
331   return _ges_add_clip_from_struct (timeline, structure, error);
332 }
333
334 static gboolean
335 _ges_command_line_formatter_add_title_clip (GESTimeline * timeline,
336     GstStructure * structure, GError ** error)
337 {
338   if (!_cleanup_fields (options[TEST_CLIP].properties, structure, error))
339     return FALSE;
340
341   gst_structure_set (structure, "type", G_TYPE_STRING, "GESTitleClip", NULL);
342   gst_structure_set (structure, "asset-id", G_TYPE_STRING, "GESTitleClip",
343       NULL);
344   GST_ERROR ("Structure: %" GST_PTR_FORMAT, structure);
345
346   return _ges_add_clip_from_struct (timeline, structure, error);
347 }
348
349 static gboolean
350 _ges_command_line_formatter_add_effect (GESTimeline * timeline,
351     GstStructure * structure, GError ** error)
352 {
353   if (!_cleanup_fields (options[EFFECT].properties, structure, error))
354     return FALSE;
355
356   gst_structure_set (structure, "child-type", G_TYPE_STRING, "GESEffect", NULL);
357
358   return _ges_container_add_child_from_struct (timeline, structure, error);
359 }
360
361 gchar *
362 ges_command_line_formatter_get_help (gint nargs, gchar ** commands)
363 {
364   gint i;
365   GString *help = g_string_new (NULL);
366
367   for (i = 0; i < G_N_ELEMENTS (options); i++) {
368     gboolean print = nargs == 0;
369     GESCommandLineOption option = options[i];
370
371     if (!print) {
372       gint j;
373
374       for (j = 0; j < nargs; j++) {
375         gchar *cname = commands[j][0] == '+' ? &commands[j][1] : commands[j];
376
377         if (!g_strcmp0 (cname, option.long_name)) {
378           print = TRUE;
379           break;
380         }
381       }
382     }
383
384     if (print) {
385       g_string_append_printf (help, "%s%s %s\n",
386           option.properties[0].long_name ? "+" : "",
387           option.long_name, option.description);
388
389       if (option.properties[0].long_name) {
390         gint j;
391
392         g_string_append (help, "  Properties:\n");
393
394         for (j = 1; option.properties[j].long_name; j++) {
395           Property prop = option.properties[j];
396           g_string_append_printf (help, "    * %s: %s\n", prop.long_name,
397               prop.desc);
398         }
399       }
400
401       g_string_append (help, "\n");
402     }
403   }
404
405   return g_string_free (help, FALSE);
406 }
407
408
409 static gboolean
410 _set_child_property (GESTimeline * timeline, GstStructure * structure,
411     GError ** error)
412 {
413   return _ges_set_child_property_from_struct (timeline, structure, error);
414 }
415
416 #define EXEC(func,structure,error) G_STMT_START { \
417   gboolean res = ((ActionFromStructureFunc)func)(timeline, structure, error); \
418   if (!res) {\
419     GST_ERROR ("Could not execute: %" GST_PTR_FORMAT ", error: %s", structure, (*error)->message); \
420     goto fail; \
421   } \
422 } G_STMT_END
423
424
425 static GESStructureParser *
426 _parse_structures (const gchar * string)
427 {
428   yyscan_t scanner;
429   GESStructureParser *parser = ges_structure_parser_new ();
430
431   priv_ges_parse_yylex_init_extra (parser, &scanner);
432   priv_ges_parse_yy_scan_string (string, scanner);
433   priv_ges_parse_yylex (scanner);
434   priv_ges_parse_yylex_destroy (scanner);
435
436   ges_structure_parser_end_of_file (parser);
437   return parser;
438 }
439
440 static gboolean
441 _can_load (GESFormatter * dummy_formatter, const gchar * string,
442     GError ** error)
443 {
444   gboolean res = FALSE;
445   GESStructureParser *parser;
446
447   if (string == NULL)
448     return FALSE;
449
450   parser = _parse_structures (string);
451
452   if (parser->structures)
453     res = TRUE;
454
455   gst_object_unref (parser);
456
457   return res;
458 }
459
460 static gboolean
461 _load (GESFormatter * self, GESTimeline * timeline, const gchar * string,
462     GError ** error)
463 {
464   guint i;
465   GList *tmp;
466   GError *err;
467   GESStructureParser *parser = _parse_structures (string);
468
469   err = ges_structure_parser_get_error (parser);
470
471   if (err) {
472     if (error)
473       *error = err;
474
475     return FALSE;
476   }
477
478   g_object_set (timeline, "auto-transition", TRUE, NULL);
479   if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_video_track_new ()))))
480     goto fail;
481
482   if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_audio_track_new ()))))
483     goto fail;
484
485   /* Here we've finished initializing our timeline, we're
486    * ready to start using it... by solely working with the layer !*/
487   for (tmp = parser->structures; tmp; tmp = tmp->next) {
488     const gchar *name = gst_structure_get_name (tmp->data);
489     if (g_str_has_prefix (name, "set-")) {
490       EXEC (_set_child_property, tmp->data, &err);
491       continue;
492     }
493
494     for (i = 0; i < G_N_ELEMENTS (options); i++) {
495       if (gst_structure_has_name (tmp->data, options[i].long_name)
496           || (strlen (name) == 1 && *name == options[i].short_name)) {
497         EXEC (((ActionFromStructureFunc) options[i].callback), tmp->data, &err);
498       }
499     }
500   }
501
502   gst_object_unref (parser);
503
504   return TRUE;
505
506 fail:
507   gst_object_unref (parser);
508   if (err) {
509     if (error)
510       *error = err;
511   }
512
513   return FALSE;
514 }
515
516 static void
517 ges_command_line_formatter_init (GESCommandLineFormatter * formatter)
518 {
519   formatter->priv = ges_command_line_formatter_get_instance_private (formatter);
520 }
521
522 static void
523 ges_command_line_formatter_finalize (GObject * object)
524 {
525   G_OBJECT_CLASS (ges_command_line_formatter_parent_class)->finalize (object);
526 }
527
528 static void
529 ges_command_line_formatter_class_init (GESCommandLineFormatterClass * klass)
530 {
531   GObjectClass *object_class = G_OBJECT_CLASS (klass);
532   GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (klass);
533
534   object_class->finalize = ges_command_line_formatter_finalize;
535
536   formatter_klass->can_load_uri = _can_load;
537   formatter_klass->load_from_uri = _load;
538   formatter_klass->rank = GST_RANK_MARGINAL;
539 }