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,
209 "Adds a clip in the timeline\n"
210 " * start - s : The start position of the element inside the layer.\n"
211 " * duration - d: The duration of the clip.\n"
212 " * inpoint - i : The inpoint of the clip.\n"
213 " * track-types - tt: The type of the tracks where the clip should be used:\n"
217 " * audio+video / a+v\n"
218 " Will default to all the media types in the clip that match the global track-types\n"},
219 {"effect", 'e', 0.0, G_OPTION_ARG_CALLBACK,
220 &_ges_command_line_formatter_add_effect, "",
221 "Adds an effect as specified by 'bin-description'\n"
222 " * bin-description - d: The description of the effect bin with a gst-launch-style pipeline description.\n"
223 " * element-name - e : The name of the element to apply the effect on.\n"},
224 {"test-clip", 0, 0.0, G_OPTION_ARG_CALLBACK,
225 &_ges_command_line_formatter_add_test_clip,
227 "Add a test clip in the timeline\n"
228 " * start -s : The start position of the element inside the layer.\n"
229 " * duration -d : The duration of the clip."
230 " * inpoint - i : The inpoint of the clip.\n"},
234 _ges_command_line_formatter_get_option_group (void)
238 group = g_option_group_new ("GESCommandLineFormatter",
239 "GStreamer Editing Services command line options to describe a timeline",
240 "Show GStreamer Options", NULL, NULL);
241 g_option_group_add_entries (group, timeline_parsing_options);
248 _set_child_property (GESTimeline * timeline, GstStructure * structure,
251 return _ges_set_child_property_from_struct (timeline, structure, error);
254 #define EXEC(func,structure,error) G_STMT_START { \
255 gboolean res = ((ActionFromStructureFunc)func)(timeline, structure, error); \
257 GST_ERROR ("Could not execute: %" GST_PTR_FORMAT ", error: %s", structure, (*error)->message); \
263 static GESStructureParser *
264 _parse_structures (const gchar * string)
267 GESStructureParser *parser = ges_structure_parser_new ();
269 priv_ges_parse_yylex_init_extra (parser, &scanner);
270 priv_ges_parse_yy_scan_string (string, scanner);
271 priv_ges_parse_yylex (scanner);
272 priv_ges_parse_yylex_destroy (scanner);
274 ges_structure_parser_end_of_file (parser);
279 _can_load (GESFormatter * dummy_formatter, const gchar * string,
282 gboolean res = FALSE;
283 GESStructureParser *parser;
288 parser = _parse_structures (string);
290 if (parser->structures)
293 gst_object_unref (parser);
299 _load (GESFormatter * self, GESTimeline * timeline, const gchar * string,
305 GESStructureParser *parser = _parse_structures (string);
307 err = ges_structure_parser_get_error (parser);
316 g_object_set (timeline, "auto-transition", TRUE, NULL);
317 if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_video_track_new ()))))
320 if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_audio_track_new ()))))
323 /* Here we've finished initializing our timeline, we're
324 * ready to start using it... by solely working with the layer !*/
325 for (tmp = parser->structures; tmp; tmp = tmp->next) {
326 const gchar *name = gst_structure_get_name (tmp->data);
327 if (g_str_has_prefix (name, "set-")) {
328 EXEC (_set_child_property, tmp->data, &err);
332 for (i = 0; i < G_N_ELEMENTS (timeline_parsing_options); i++) {
333 if (gst_structure_has_name (tmp->data,
334 timeline_parsing_options[i].long_name)
335 || (strlen (name) == 1 &&
336 *name == timeline_parsing_options[i].short_name)) {
337 EXEC (((ActionFromStructureFunc) timeline_parsing_options[i].arg_data),
343 gst_object_unref (parser);
348 gst_object_unref (parser);
358 ges_command_line_formatter_init (GESCommandLineFormatter *
359 ges_command_line_formatter)
361 ges_command_line_formatter->priv =
362 G_TYPE_INSTANCE_GET_PRIVATE (ges_command_line_formatter,
363 GES_TYPE_COMMAND_LINE_FORMATTER, GESCommandLineFormatterPrivate);
365 /* TODO: Add initialization code here */
369 ges_command_line_formatter_finalize (GObject * object)
371 /* TODO: Add deinitalization code here */
373 G_OBJECT_CLASS (ges_command_line_formatter_parent_class)->finalize (object);
377 ges_command_line_formatter_class_init (GESCommandLineFormatterClass * klass)
379 GObjectClass *object_class = G_OBJECT_CLASS (klass);
380 GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (klass);
382 g_type_class_add_private (klass, sizeof (GESCommandLineFormatterPrivate));
384 object_class->finalize = ges_command_line_formatter_finalize;
386 formatter_klass->can_load_uri = _can_load;
387 formatter_klass->load_from_uri = _load;
388 formatter_klass->rank = GST_RANK_MARGINAL;