1 /* GStreamer Editing Services
3 * Copyright (C) <2015> Thibault Saunier <tsaunier@gnome.org>
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.
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.
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.
21 #include "ges-command-line-formatter.h"
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"
29 struct _GESCommandLineFormatterPrivate
35 G_DEFINE_TYPE_WITH_PRIVATE (GESCommandLineFormatter, ges_command_line_formatter,
39 _ges_command_line_formatter_add_clip (GESTimeline * timeline,
40 GstStructure * structure, GError ** error);
42 _ges_command_line_formatter_add_effect (GESTimeline * timeline,
43 GstStructure * structure, GError ** error);
45 _ges_command_line_formatter_add_test_clip (GESTimeline * timeline,
46 GstStructure * structure, GError ** error);
48 _ges_command_line_formatter_add_title_clip (GESTimeline * timeline,
49 GstStructure * structure, GError ** error);
53 const gchar *long_name;
54 const gchar *short_name;
56 const gchar *new_name;
60 // Currently Clip has the most properties.. adapt as needed
61 #define MAX_PROPERTIES 8
64 const gchar *long_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;
73 static GESCommandLineOption options[] = {
74 {"clip", 'c', (ActionFromStructureFunc) _ges_command_line_formatter_add_clip,
75 "<clip uri> - Adds a clip in the timeline.",
78 "uri", "n", 0, "asset-id",
79 "The URI of the media file."
83 "The name of the clip, can be used as an ID later."
86 "start", "s",GST_TYPE_CLOCK_TIME, NULL,
87 "The starting position of the clip in the timeline."
90 "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
91 "The duration of the clip."
94 "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
95 "The inpoint of the clip (time in the input file to start playing from)."
98 "track-types", "tt", 0, NULL,
99 "The type of the tracks where the clip should be used (audio or video or audio+video)."
102 "layer", "l", 0, NULL,
103 "The priority of the layer into which the clip should be added."
105 {NULL, 0, 0, NULL, FALSE},
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",
114 "bin-description", "d", 0, "asset-id",
115 "gst-launch style bin description."
118 "element-name", "e", 0, NULL,
119 "The name of the element to apply the effect on."
122 "name", "n", 0, "child-name",
123 "The name to be given to the effect."
125 {NULL, NULL, 0, NULL, FALSE},
128 {"test-clip", 0, (ActionFromStructureFunc) _ges_command_line_formatter_add_test_clip,
129 "<test clip pattern> - Add a test clip in the timeline.",
132 "pattern", "p", 0, NULL,
133 "The testsource pattern name."
136 "name", "n", 0, NULL,
137 "The name of the clip, can be used as an ID later."
140 "start", "s",GST_TYPE_CLOCK_TIME, NULL,
141 "The starting position of the clip in the timeline."
144 "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
145 "The duration of the clip."
148 "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
149 "The inpoint of the clip (time in the input file to start playing)."
152 "layer", "l", 0, NULL,
153 "The priority of the layer into which the clip should be added."
155 {NULL, 0, 0, NULL, FALSE},
158 {"title", 'c', (ActionFromStructureFunc) _ges_command_line_formatter_add_title_clip,
159 "<title text> - Adds a clip in the timeline.",
162 "text", "n", 0, NULL,
163 "The text to be used as title."
166 "name", "n", 0, NULL,
167 "The name of the clip, can be used as an ID later."
170 "start", "s",GST_TYPE_CLOCK_TIME, NULL,
171 "The starting position of the clip in the timeline."
174 "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
175 "The duration of the clip."
178 "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
179 "The inpoint of the clip (time in the input file to start playing from)."
182 "track-types", "tt", 0, NULL,
183 "The type of the tracks where the clip should be used (audio or video or audio+video)."
186 "layer", "l", 0, NULL,
187 "The priority of the layer into which the clip should be added."
189 {NULL, 0, 0, NULL, FALSE},
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>",
198 {NULL, NULL, 0, NULL, FALSE},
204 /* Should always be in the same order as the options */
212 } GESCommandLineOptionType;
214 static gint /* -1: not present, 0: failure, 1: OK */
215 _convert_to_clocktime (GstStructure * structure, const gchar * name,
216 GstClockTime default_value)
220 GValue d_val = { 0 };
221 GstClockTime timestamp;
222 const GValue *gvalue = gst_structure_get_value (structure, name);
224 if (gvalue == NULL) {
225 timestamp = default_value;
232 if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME)
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);
241 val = g_value_get_double ((const GValue *) &d_val);
244 timestamp = GST_CLOCK_TIME_NONE;
246 timestamp = val * GST_SECOND;
249 gst_structure_set (structure, name, G_TYPE_UINT64, timestamp, NULL);
255 _cleanup_fields (const Property * field_names, GstStructure * structure,
260 for (i = 0; field_names[i].long_name; i++) {
261 gboolean exists = FALSE;
263 /* Move shortly named fields to longname variante */
264 if (gst_structure_has_field (structure, field_names[i].short_name)) {
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);
275 gst_structure_get_value (structure, field_names[i].short_name);
277 gst_structure_set_value (structure, field_names[i].long_name, val);
278 gst_structure_remove_field (structure, field_names[i].short_name);
280 } else if (gst_structure_has_field (structure, field_names[i].long_name)) {
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);
295 if (field_names[i].new_name
296 && gst_structure_has_field (structure, field_names[i].long_name)) {
298 gst_structure_get_value (structure, field_names[i].long_name);
300 gst_structure_set_value (structure, field_names[i].new_name, val);
301 gst_structure_remove_field (structure, field_names[i].long_name);
309 _ges_command_line_formatter_add_clip (GESTimeline * timeline,
310 GstStructure * structure, GError ** error)
312 if (!_cleanup_fields (options[CLIP].properties, structure, error))
315 gst_structure_set (structure, "type", G_TYPE_STRING, "GESUriClip", NULL);
317 return _ges_add_clip_from_struct (timeline, structure, error);
321 _ges_command_line_formatter_add_test_clip (GESTimeline * timeline,
322 GstStructure * structure, GError ** error)
324 if (!_cleanup_fields (options[TEST_CLIP].properties, structure, error))
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);
331 return _ges_add_clip_from_struct (timeline, structure, error);
335 _ges_command_line_formatter_add_title_clip (GESTimeline * timeline,
336 GstStructure * structure, GError ** error)
338 if (!_cleanup_fields (options[TEST_CLIP].properties, structure, error))
341 gst_structure_set (structure, "type", G_TYPE_STRING, "GESTitleClip", NULL);
342 gst_structure_set (structure, "asset-id", G_TYPE_STRING, "GESTitleClip",
344 GST_ERROR ("Structure: %" GST_PTR_FORMAT, structure);
346 return _ges_add_clip_from_struct (timeline, structure, error);
350 _ges_command_line_formatter_add_effect (GESTimeline * timeline,
351 GstStructure * structure, GError ** error)
353 if (!_cleanup_fields (options[EFFECT].properties, structure, error))
356 gst_structure_set (structure, "child-type", G_TYPE_STRING, "GESEffect", NULL);
358 return _ges_container_add_child_from_struct (timeline, structure, error);
362 ges_command_line_formatter_get_help (gint nargs, gchar ** commands)
365 GString *help = g_string_new (NULL);
367 for (i = 0; i < G_N_ELEMENTS (options); i++) {
368 gboolean print = nargs == 0;
369 GESCommandLineOption option = options[i];
374 for (j = 0; j < nargs; j++) {
375 gchar *cname = commands[j][0] == '+' ? &commands[j][1] : commands[j];
377 if (!g_strcmp0 (cname, option.long_name)) {
385 g_string_append_printf (help, "%s%s %s\n",
386 option.properties[0].long_name ? "+" : "",
387 option.long_name, option.description);
389 if (option.properties[0].long_name) {
392 g_string_append (help, " Properties:\n");
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,
401 g_string_append (help, "\n");
405 return g_string_free (help, FALSE);
410 _set_child_property (GESTimeline * timeline, GstStructure * structure,
413 return _ges_set_child_property_from_struct (timeline, structure, error);
416 #define EXEC(func,structure,error) G_STMT_START { \
417 gboolean res = ((ActionFromStructureFunc)func)(timeline, structure, error); \
419 GST_ERROR ("Could not execute: %" GST_PTR_FORMAT ", error: %s", structure, (*error)->message); \
425 static GESStructureParser *
426 _parse_structures (const gchar * string)
429 GESStructureParser *parser = ges_structure_parser_new ();
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);
436 ges_structure_parser_end_of_file (parser);
441 _can_load (GESFormatter * dummy_formatter, const gchar * string,
444 gboolean res = FALSE;
445 GESStructureParser *parser;
450 parser = _parse_structures (string);
452 if (parser->structures)
455 gst_object_unref (parser);
461 _load (GESFormatter * self, GESTimeline * timeline, const gchar * string,
467 GESStructureParser *parser = _parse_structures (string);
469 err = ges_structure_parser_get_error (parser);
478 g_object_set (timeline, "auto-transition", TRUE, NULL);
479 if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_video_track_new ()))))
482 if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_audio_track_new ()))))
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);
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);
502 gst_object_unref (parser);
507 gst_object_unref (parser);
517 ges_command_line_formatter_init (GESCommandLineFormatter * formatter)
519 formatter->priv = ges_command_line_formatter_get_instance_private (formatter);
523 ges_command_line_formatter_finalize (GObject * object)
525 G_OBJECT_CLASS (ges_command_line_formatter_parent_class)->finalize (object);
529 ges_command_line_formatter_class_init (GESCommandLineFormatterClass * klass)
531 GObjectClass *object_class = G_OBJECT_CLASS (klass);
532 GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (klass);
534 object_class->finalize = ges_command_line_formatter_finalize;
536 formatter_klass->can_load_uri = _can_load;
537 formatter_klass->load_from_uri = _load;
538 formatter_klass->rank = GST_RANK_MARGINAL;