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 #include "parse_lex.h"
28 struct _GESCommandLineFormatterPrivate
34 G_DEFINE_TYPE (GESCommandLineFormatter, ges_command_line_formatter,
39 const gchar *long_name;
40 const gchar *short_name;
42 const gchar *new_name;
45 static gint /* -1: not present, 0: failure, 1: OK */
46 _convert_to_clocktime (GstStructure * structure, const gchar * name,
47 GstClockTime default_value)
52 GstClockTime timestamp;
53 const GValue *gvalue = gst_structure_get_value (structure, name);
56 timestamp = default_value;
63 if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME)
66 g_value_init (&d_val, G_TYPE_DOUBLE);
67 if (!g_value_transform (gvalue, &d_val)) {
68 GST_ERROR ("Could not get timestamp for %s", name);
72 val = g_value_get_double ((const GValue *) &d_val);
75 timestamp = GST_CLOCK_TIME_NONE;
77 timestamp = val * GST_SECOND;
80 gst_structure_set (structure, name, G_TYPE_UINT64, timestamp, NULL);
86 _cleanup_fields (const Properties * field_names, GstStructure * structure,
91 for (i = 0; field_names[i].long_name; i++) {
92 gboolean exists = FALSE;
94 /* Move shortly named fields to longname variante */
95 if (gst_structure_has_field (structure, field_names[i].short_name)) {
98 if (gst_structure_has_field (structure, field_names[i].long_name)) {
99 *error = g_error_new (GES_ERROR, 0, "Using short and long name"
100 " at the same time for property: %s, which one should I use?!",
101 field_names[i].long_name);
106 gst_structure_get_value (structure, field_names[i].short_name);
108 gst_structure_set_value (structure, field_names[i].long_name, val);
109 gst_structure_remove_field (structure, field_names[i].short_name);
111 } else if (gst_structure_has_field (structure, field_names[i].long_name)) {
116 if (field_names[i].type == GST_TYPE_CLOCK_TIME) {
117 if (_convert_to_clocktime (structure, field_names[i].long_name, 0) == 0) {
118 *error = g_error_new (GES_ERROR, 0, "Could not convert"
119 " %s to GstClockTime", field_names[i].long_name);
126 if (field_names[i].new_name
127 && gst_structure_has_field (structure, field_names[i].long_name)) {
129 gst_structure_get_value (structure, field_names[i].long_name);
131 gst_structure_set_value (structure, field_names[i].new_name, val);
132 gst_structure_remove_field (structure, field_names[i].long_name);
140 _ges_command_line_formatter_add_clip (GESTimeline * timeline,
141 GstStructure * structure, GError ** error)
143 const Properties field_names[] = {
144 {"uri", "n", 0, "asset-id"},
145 {"name", "n", 0, NULL},
146 {"start", "s", GST_TYPE_CLOCK_TIME, NULL},
147 {"duration", "d", GST_TYPE_CLOCK_TIME, NULL},
148 {"inpoint", "i", GST_TYPE_CLOCK_TIME, NULL},
149 {"track-types", "tt", 0, NULL},
150 {"layer", "l", 0, NULL},
154 if (!_cleanup_fields (field_names, structure, error))
157 gst_structure_set (structure, "type", G_TYPE_STRING, "GESUriClip", NULL);
159 return _ges_add_clip_from_struct (timeline, structure, error);
163 _ges_command_line_formatter_add_test_clip (GESTimeline * timeline,
164 GstStructure * structure, GError ** error)
166 const Properties field_names[] = {
167 {"pattern", "p", G_TYPE_STRING, NULL},
168 {"name", "n", 0, NULL},
169 {"start", "s", GST_TYPE_CLOCK_TIME, NULL},
170 {"duration", "d", GST_TYPE_CLOCK_TIME, NULL},
171 {"inpoint", "i", GST_TYPE_CLOCK_TIME, NULL},
172 {"layer", "l", 0, NULL},
176 if (!_cleanup_fields (field_names, structure, error))
179 gst_structure_set (structure, "type", G_TYPE_STRING, "GESTestClip", NULL);
180 gst_structure_set (structure, "asset-id", G_TYPE_STRING,
181 gst_structure_get_string (structure, "pattern"), NULL);
183 return _ges_add_clip_from_struct (timeline, structure, error);
187 _ges_command_line_formatter_add_effect (GESTimeline * timeline,
188 GstStructure * structure, GError ** error)
190 const Properties field_names[] = {
191 {"element-name", "e", 0, NULL},
192 {"bin-description", "d", 0, "asset-id"},
193 {"name", "n", 0, "child-name"},
194 {NULL, NULL, 0, NULL},
197 if (!_cleanup_fields (field_names, structure, error))
200 gst_structure_set (structure, "child-type", G_TYPE_STRING, "GESEffect", NULL);
202 return _ges_container_add_child_from_struct (timeline, structure, error);
205 static GOptionEntry timeline_parsing_options[] = {
206 {"clip", 'c', 0.0, G_OPTION_ARG_CALLBACK,
207 &_ges_command_line_formatter_add_clip,
208 "<clip uri> - Adds a clip in the timeline.",
209 " * s=, start The start position of the element inside the layer.\n"
210 " * d=, duration The duration of the clip.\n"
211 " * i=, inpoint The inpoint of the clip.\n"
212 " * tt=, track-types The type of the tracks where the clip should be used:\n"
216 " * audio+video / a+v\n"
217 " (Will default to all the media types in the clip that match the global track-types)\n"},
218 {"effect", 'e', 0.0, G_OPTION_ARG_CALLBACK,
219 &_ges_command_line_formatter_add_effect,
220 "<effect bin description> - Adds an effect as specified by 'bin-description'.",
221 " * d=, bin-description The description of the effect bin with a gst-launch-style pipeline description.\n"
222 " * e=, element-name The name of the element to apply the effect on.\n"},
223 {"test-clip", 0, 0.0, G_OPTION_ARG_CALLBACK,
224 &_ges_command_line_formatter_add_test_clip,
225 "<test clip pattern> - Add a test clip in the timeline.",
226 " * s=, start The start position of the element inside the layer.\n"
227 " * d=, duration The duration of the clip.\n"
228 " * i=, inpoint The inpoint of the clip.\n"},
229 {"set-", 0, 0.0, G_OPTION_ARG_CALLBACK,
231 "<property name> <value> - Set a property on the last added element."
232 " Any child property that exists on the previously added element"
233 " can be used as <property name>", NULL},
237 ges_command_line_formatter_get_help (gint nargs, gchar ** commands)
242 for (i = 0; i < G_N_ELEMENTS (timeline_parsing_options); i++) {
243 gboolean print = nargs == 0;
248 for (j = 0; j < nargs; j++) {
249 gchar *cname = commands[j][0] == '+' ? &commands[j][1] : commands[j];
251 if (!g_strcmp0 (cname, timeline_parsing_options[i].long_name)) {
259 gchar *tmp = g_strdup_printf ("%s %s%s %s\n", help ? help : "",
260 timeline_parsing_options[i].arg_description ? "+" : "",
261 timeline_parsing_options[i].long_name,
262 timeline_parsing_options[i].description);
267 if (timeline_parsing_options[i].arg_description) {
268 tmp = g_strdup_printf ("%s Properties:\n%s\n", help,
269 timeline_parsing_options[i].arg_description);
281 _set_child_property (GESTimeline * timeline, GstStructure * structure,
284 return _ges_set_child_property_from_struct (timeline, structure, error);
287 #define EXEC(func,structure,error) G_STMT_START { \
288 gboolean res = ((ActionFromStructureFunc)func)(timeline, structure, error); \
290 GST_ERROR ("Could not execute: %" GST_PTR_FORMAT ", error: %s", structure, (*error)->message); \
296 static GESStructureParser *
297 _parse_structures (const gchar * string)
300 GESStructureParser *parser = ges_structure_parser_new ();
302 priv_ges_parse_yylex_init_extra (parser, &scanner);
303 priv_ges_parse_yy_scan_string (string, scanner);
304 priv_ges_parse_yylex (scanner);
305 priv_ges_parse_yylex_destroy (scanner);
307 ges_structure_parser_end_of_file (parser);
312 _can_load (GESFormatter * dummy_formatter, const gchar * string,
315 gboolean res = FALSE;
316 GESStructureParser *parser;
321 parser = _parse_structures (string);
323 if (parser->structures)
326 gst_object_unref (parser);
332 _load (GESFormatter * self, GESTimeline * timeline, const gchar * string,
338 GESStructureParser *parser = _parse_structures (string);
340 err = ges_structure_parser_get_error (parser);
349 g_object_set (timeline, "auto-transition", TRUE, NULL);
350 if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_video_track_new ()))))
353 if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_audio_track_new ()))))
356 /* Here we've finished initializing our timeline, we're
357 * ready to start using it... by solely working with the layer !*/
358 for (tmp = parser->structures; tmp; tmp = tmp->next) {
359 const gchar *name = gst_structure_get_name (tmp->data);
360 if (g_str_has_prefix (name, "set-")) {
361 EXEC (_set_child_property, tmp->data, &err);
365 for (i = 0; i < G_N_ELEMENTS (timeline_parsing_options); i++) {
366 if (gst_structure_has_name (tmp->data,
367 timeline_parsing_options[i].long_name)
368 || (strlen (name) == 1 &&
369 *name == timeline_parsing_options[i].short_name)) {
370 EXEC (((ActionFromStructureFunc) timeline_parsing_options[i].arg_data),
376 gst_object_unref (parser);
381 gst_object_unref (parser);
391 ges_command_line_formatter_init (GESCommandLineFormatter *
392 ges_command_line_formatter)
394 ges_command_line_formatter->priv =
395 G_TYPE_INSTANCE_GET_PRIVATE (ges_command_line_formatter,
396 GES_TYPE_COMMAND_LINE_FORMATTER, GESCommandLineFormatterPrivate);
398 /* TODO: Add initialization code here */
402 ges_command_line_formatter_finalize (GObject * object)
404 /* TODO: Add deinitalization code here */
406 G_OBJECT_CLASS (ges_command_line_formatter_parent_class)->finalize (object);
410 ges_command_line_formatter_class_init (GESCommandLineFormatterClass * klass)
412 GObjectClass *object_class = G_OBJECT_CLASS (klass);
413 GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (klass);
415 g_type_class_add_private (klass, sizeof (GESCommandLineFormatterPrivate));
417 object_class->finalize = ges_command_line_formatter_finalize;
419 formatter_klass->can_load_uri = _can_load;
420 formatter_klass->load_from_uri = _load;
421 formatter_klass->rank = GST_RANK_MARGINAL;