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 (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);
50 const gchar *long_name;
51 const gchar *short_name;
53 const gchar *new_name;
57 // Currently Clip has the most properties.. adapt as needed
58 #define MAX_PROPERTIES 8
61 const gchar *long_name;
63 ActionFromStructureFunc callback;
64 const gchar *description;
65 /* The first property must be the ID on the command line */
66 Property properties[MAX_PROPERTIES];
67 } GESCommandLineOption;
70 static GESCommandLineOption options[] = {
71 {"clip", 'c', (ActionFromStructureFunc) _ges_command_line_formatter_add_clip,
72 "<clip uri> - Adds a clip in the timeline.",
75 "uri", "n", 0, "asset-id",
76 "The URI of the media file."
80 "The name of the clip, can be used as an ID later."
83 "start", "s",GST_TYPE_CLOCK_TIME, NULL,
84 "The starting position of the clip in the timeline."
87 "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
88 "The duration of the clip."
91 "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
92 "The inpoint of the clip (time in the input file to start playing from)."
95 "track-types", "tt", 0, NULL,
96 "The type of the tracks where the clip should be used (audio or video or audio+video)."
99 "layer", "l", 0, NULL,
100 "The priority of the layer into which the clip should be added."
102 {NULL, 0, 0, NULL, FALSE},
105 {"effect", 'e', (ActionFromStructureFunc) _ges_command_line_formatter_add_effect,
106 "<effect bin description> - Adds an effect as specified by 'bin-description',\n"
107 "similar to gst-launch-style pipeline description, without setting properties\n"
108 "(see `set-` for information about how to set properties).\n",
111 "bin-description", "d", 0, "asset-id",
112 "gst-launch style bin description."
115 "element-name", "e", 0, NULL,
116 "The name of the element to apply the effect on."
119 "name", "n", 0, "child-name",
120 "The name to be given to the effect."
122 {NULL, NULL, 0, NULL, FALSE},
125 {"test-clip", 0, (ActionFromStructureFunc) _ges_command_line_formatter_add_test_clip,
126 "<test clip pattern> - Add a test clip in the timeline.",
129 "pattern", "p", 0, NULL,
130 "The testsource pattern name."
133 "name", "n", 0, NULL,
134 "The name of the clip, can be used as an ID later."
137 "start", "s",GST_TYPE_CLOCK_TIME, NULL,
138 "The starting position of the clip in the timeline."
141 "duration", "d", GST_TYPE_CLOCK_TIME, NULL,
142 "The duration of the clip."
145 "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
146 "The inpoint of the clip (time in the input file to start playing)."
149 "layer", "l", 0, NULL,
150 "The priority of the layer into which the clip should be added."
152 {NULL, 0, 0, NULL, FALSE},
157 "<property name> <value> - Set a property on the last added element.\n"
158 "Any child property that exists on the previously added element\n"
159 "can be used as <property name>",
161 {NULL, NULL, 0, NULL, FALSE},
167 /* Should always be in the same order as the options */
174 } GESCommandLineOptionType;
176 static gint /* -1: not present, 0: failure, 1: OK */
177 _convert_to_clocktime (GstStructure * structure, const gchar * name,
178 GstClockTime default_value)
182 GValue d_val = { 0 };
183 GstClockTime timestamp;
184 const GValue *gvalue = gst_structure_get_value (structure, name);
186 if (gvalue == NULL) {
187 timestamp = default_value;
194 if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME)
197 g_value_init (&d_val, G_TYPE_DOUBLE);
198 if (!g_value_transform (gvalue, &d_val)) {
199 GST_ERROR ("Could not get timestamp for %s", name);
203 val = g_value_get_double ((const GValue *) &d_val);
206 timestamp = GST_CLOCK_TIME_NONE;
208 timestamp = val * GST_SECOND;
211 gst_structure_set (structure, name, G_TYPE_UINT64, timestamp, NULL);
217 _cleanup_fields (const Property * field_names, GstStructure * structure,
222 for (i = 0; field_names[i].long_name; i++) {
223 gboolean exists = FALSE;
225 /* Move shortly named fields to longname variante */
226 if (gst_structure_has_field (structure, field_names[i].short_name)) {
229 if (gst_structure_has_field (structure, field_names[i].long_name)) {
230 *error = g_error_new (GES_ERROR, 0, "Using short and long name"
231 " at the same time for property: %s, which one should I use?!",
232 field_names[i].long_name);
237 gst_structure_get_value (structure, field_names[i].short_name);
239 gst_structure_set_value (structure, field_names[i].long_name, val);
240 gst_structure_remove_field (structure, field_names[i].short_name);
242 } else if (gst_structure_has_field (structure, field_names[i].long_name)) {
247 if (field_names[i].type == GST_TYPE_CLOCK_TIME) {
248 if (_convert_to_clocktime (structure, field_names[i].long_name, 0) == 0) {
249 *error = g_error_new (GES_ERROR, 0, "Could not convert"
250 " %s to GstClockTime", field_names[i].long_name);
257 if (field_names[i].new_name
258 && gst_structure_has_field (structure, field_names[i].long_name)) {
260 gst_structure_get_value (structure, field_names[i].long_name);
262 gst_structure_set_value (structure, field_names[i].new_name, val);
263 gst_structure_remove_field (structure, field_names[i].long_name);
271 _ges_command_line_formatter_add_clip (GESTimeline * timeline,
272 GstStructure * structure, GError ** error)
274 if (!_cleanup_fields (options[CLIP].properties, structure, error))
277 gst_structure_set (structure, "type", G_TYPE_STRING, "GESUriClip", NULL);
279 return _ges_add_clip_from_struct (timeline, structure, error);
283 _ges_command_line_formatter_add_test_clip (GESTimeline * timeline,
284 GstStructure * structure, GError ** error)
286 if (!_cleanup_fields (options[TEST_CLIP].properties, structure, error))
289 gst_structure_set (structure, "type", G_TYPE_STRING, "GESTestClip", NULL);
290 gst_structure_set (structure, "asset-id", G_TYPE_STRING,
291 gst_structure_get_string (structure, "pattern"), NULL);
293 return _ges_add_clip_from_struct (timeline, structure, error);
297 _ges_command_line_formatter_add_effect (GESTimeline * timeline,
298 GstStructure * structure, GError ** error)
300 if (!_cleanup_fields (options[EFFECT].properties, structure, error))
303 gst_structure_set (structure, "child-type", G_TYPE_STRING, "GESEffect", NULL);
305 return _ges_container_add_child_from_struct (timeline, structure, error);
309 ges_command_line_formatter_get_help (gint nargs, gchar ** commands)
312 GString *help = g_string_new (NULL);
314 for (i = 0; i < G_N_ELEMENTS (options); i++) {
315 gboolean print = nargs == 0;
316 GESCommandLineOption option = options[i];
321 for (j = 0; j < nargs; j++) {
322 gchar *cname = commands[j][0] == '+' ? &commands[j][1] : commands[j];
324 if (!g_strcmp0 (cname, option.long_name)) {
332 g_string_append_printf (help, "%s%s %s\n",
333 option.properties[0].long_name ? "+" : "",
334 option.long_name, option.description);
336 if (option.properties[0].long_name) {
339 g_string_append (help, " Properties:\n");
341 for (j = 1; option.properties[j].long_name; j++) {
342 Property prop = option.properties[j];
343 g_string_append_printf (help, " * %s: %s\n", prop.long_name,
348 g_string_append (help, "\n");
352 return g_string_free (help, FALSE);
357 _set_child_property (GESTimeline * timeline, GstStructure * structure,
360 return _ges_set_child_property_from_struct (timeline, structure, error);
363 #define EXEC(func,structure,error) G_STMT_START { \
364 gboolean res = ((ActionFromStructureFunc)func)(timeline, structure, error); \
366 GST_ERROR ("Could not execute: %" GST_PTR_FORMAT ", error: %s", structure, (*error)->message); \
372 static GESStructureParser *
373 _parse_structures (const gchar * string)
376 GESStructureParser *parser = ges_structure_parser_new ();
378 priv_ges_parse_yylex_init_extra (parser, &scanner);
379 priv_ges_parse_yy_scan_string (string, scanner);
380 priv_ges_parse_yylex (scanner);
381 priv_ges_parse_yylex_destroy (scanner);
383 ges_structure_parser_end_of_file (parser);
388 _can_load (GESFormatter * dummy_formatter, const gchar * string,
391 gboolean res = FALSE;
392 GESStructureParser *parser;
397 parser = _parse_structures (string);
399 if (parser->structures)
402 gst_object_unref (parser);
408 _load (GESFormatter * self, GESTimeline * timeline, const gchar * string,
414 GESStructureParser *parser = _parse_structures (string);
416 err = ges_structure_parser_get_error (parser);
425 g_object_set (timeline, "auto-transition", TRUE, NULL);
426 if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_video_track_new ()))))
429 if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_audio_track_new ()))))
432 /* Here we've finished initializing our timeline, we're
433 * ready to start using it... by solely working with the layer !*/
434 for (tmp = parser->structures; tmp; tmp = tmp->next) {
435 const gchar *name = gst_structure_get_name (tmp->data);
436 if (g_str_has_prefix (name, "set-")) {
437 EXEC (_set_child_property, tmp->data, &err);
441 for (i = 0; i < G_N_ELEMENTS (options); i++) {
442 if (gst_structure_has_name (tmp->data, options[i].long_name)
443 || (strlen (name) == 1 && *name == options[i].short_name)) {
444 EXEC (((ActionFromStructureFunc) options[i].callback), tmp->data, &err);
449 gst_object_unref (parser);
454 gst_object_unref (parser);
464 ges_command_line_formatter_init (GESCommandLineFormatter *
465 ges_command_line_formatter)
467 ges_command_line_formatter->priv =
468 G_TYPE_INSTANCE_GET_PRIVATE (ges_command_line_formatter,
469 GES_TYPE_COMMAND_LINE_FORMATTER, GESCommandLineFormatterPrivate);
471 /* TODO: Add initialization code here */
475 ges_command_line_formatter_finalize (GObject * object)
477 /* TODO: Add deinitalization code here */
479 G_OBJECT_CLASS (ges_command_line_formatter_parent_class)->finalize (object);
483 ges_command_line_formatter_class_init (GESCommandLineFormatterClass * klass)
485 GObjectClass *object_class = G_OBJECT_CLASS (klass);
486 GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (klass);
488 g_type_class_add_private (klass, sizeof (GESCommandLineFormatterPrivate));
490 object_class->finalize = ges_command_line_formatter_finalize;
492 formatter_klass->can_load_uri = _can_load;
493 formatter_klass->load_from_uri = _load;
494 formatter_klass->rank = GST_RANK_MARGINAL;