command-line-formatter: Add track management to timeline description
authorThibault Saunier <tsaunier@igalia.com>
Tue, 12 Jan 2021 18:55:52 +0000 (15:55 -0300)
committerThibault Saunier <tsaunier@igalia.com>
Tue, 9 Feb 2021 21:11:05 +0000 (18:11 -0300)
Instead of having it all handled by the tool, this way we can
set the restriction before clips are added to the timeline,
leading to better behavior in term of video images placement
in the scene.

Without that we would have the clips positioned before setting the
restriction caps which leads to weird behavior for the end users.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/merge_requests/227>

ges/ges-command-line-formatter.c
ges/ges-structure-parser.c
ges/ges-structured-interface.c
ges/ges-structured-interface.h
ges/parse.l
tools/ges-launcher.c

index d315bb6..b97eab0 100644 (file)
@@ -50,6 +50,9 @@ _ges_command_line_formatter_add_test_clip (GESTimeline * timeline,
 static gboolean
 _ges_command_line_formatter_add_title_clip (GESTimeline * timeline,
     GstStructure * structure, GError ** error);
+static gboolean
+_ges_command_line_formatter_add_track (GESTimeline * timeline,
+    GstStructure * structure, GError ** error);
 
 typedef struct
 {
@@ -232,6 +235,16 @@ static GESCommandLineOption options[] = {
       {NULL, 0, 0, NULL, FALSE},
     },
   },
+  {"track", 't', (ActionFromStructureFunc) _ges_command_line_formatter_add_track,
+    "<track type>", "Adds a track to the timeline.", NULL,
+    {
+      {
+        "restrictions", "r", 0, NULL,
+        "The restriction caps to set on the track."
+      },
+      {NULL, 0, 0, NULL, FALSE},
+    },
+  },
   {
     "set-", 0, NULL,
     "<property name> <value>", "Set a property on the last added element."
@@ -259,6 +272,7 @@ typedef enum
   EFFECT,
   TEST_CLIP,
   TITLE,
+  TRACK,
   SET,
 } GESCommandLineOptionType;
 
@@ -442,6 +456,16 @@ _ges_command_line_formatter_add_title_clip (GESTimeline * timeline,
 }
 
 static gboolean
+_ges_command_line_formatter_add_track (GESTimeline * timeline,
+    GstStructure * structure, GError ** error)
+{
+  if (!_cleanup_fields (options[TRACK].properties, structure, error))
+    return FALSE;
+
+  return _ges_add_track_from_struct (timeline, structure, error);
+}
+
+static gboolean
 _ges_command_line_formatter_add_effect (GESTimeline * timeline,
     GstStructure * structure, GError ** error)
 {
@@ -603,11 +627,6 @@ _load (GESFormatter * self, GESTimeline * timeline, const gchar * string,
   }
 
   g_object_set (timeline, "auto-transition", TRUE, NULL);
-  if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_video_track_new ()))))
-    goto fail;
-
-  if (!(ges_timeline_add_track (timeline, GES_TRACK (ges_audio_track_new ()))))
-    goto fail;
 
   /* Here we've finished initializing our timeline, we're
    * ready to start using it... by solely working with the layer !*/
index 25831be..327b2a2 100644 (file)
@@ -99,25 +99,28 @@ ges_structure_parser_parse_whitespace (GESStructureParser * self)
 static void
 _finish_structure (GESStructureParser * self)
 {
-  if (self->current_string) {
-    GstStructure *structure =
-        gst_structure_new_from_string (self->current_string);
+  GstStructure *structure;
 
-    if (structure == NULL) {
-      GST_ERROR ("Could not parse %s", self->current_string);
+  if (!self->current_string)
+    return;
 
-      self->wrong_strings = g_list_append (self->wrong_strings,
-          g_strdup (self->current_string));
+  structure = gst_structure_new_from_string (self->current_string);
 
-      return;
-    }
+  if (structure == NULL) {
+    GST_ERROR ("Could not parse %s", self->current_string);
 
-    self->structures = g_list_append (self->structures, structure);
-    g_free (self->current_string);
-    self->current_string = NULL;
+    self->wrong_strings = g_list_append (self->wrong_strings,
+        g_strdup (self->current_string));
+
+    return;
   }
+
+  self->structures = g_list_append (self->structures, structure);
+  g_free (self->current_string);
+  self->current_string = NULL;
 }
 
+
 void
 ges_structure_parser_end_of_file (GESStructureParser * self)
 {
@@ -146,6 +149,8 @@ ges_structure_parser_parse_symbol (GESStructureParser * self,
     ges_structure_parser_parse_string (self, "transition, type=(string)", TRUE);
   else if (!g_ascii_strncasecmp (symbol, "title", 5))
     ges_structure_parser_parse_string (self, "title, text=(string)", TRUE);
+  else if (!g_ascii_strncasecmp (symbol, "track", 5))
+    ges_structure_parser_parse_string (self, "track, type=(string)", TRUE);
 }
 
 void
index b96d1a9..ff44aa7 100644 (file)
@@ -570,6 +570,69 @@ beach:
 }
 
 gboolean
+_ges_add_track_from_struct (GESTimeline * timeline,
+    GstStructure * structure, GError ** error)
+{
+  const gchar *ttype;
+  GESTrack *track;
+  GstCaps *caps;
+
+  const gchar *valid_fields[] = { "type", "restrictions", NULL };
+
+  FieldsError fields_error = { valid_fields, NULL };
+
+  if (!_check_fields (structure, fields_error, error))
+    return FALSE;
+
+  ttype = gst_structure_get_string (structure, "type");
+  if (!g_strcmp0 (ttype, "video")) {
+    track = GES_TRACK (ges_video_track_new ());
+  } else if (!g_strcmp0 (ttype, "audio")) {
+    track = GES_TRACK (ges_audio_track_new ());
+  } else {
+    g_set_error (error, GES_ERROR, 0, "Unhandled track type: `%s`", ttype);
+
+    return FALSE;
+  }
+
+  if (gst_structure_has_field (structure, "restrictions")) {
+    GstStructure *restriction_struct;
+    gchar *restriction_str;
+
+    if (gst_structure_get (structure, "restrictions", GST_TYPE_STRUCTURE,
+            &restriction_struct, NULL)) {
+      caps = gst_caps_new_full (restriction_struct, NULL);
+    } else if (gst_structure_get (structure, "restrictions", G_TYPE_STRING,
+            &restriction_str, NULL)) {
+      caps = gst_caps_from_string (restriction_str);
+
+      if (!caps) {
+        g_set_error (error, GES_ERROR, 0, "Invalid restrictions caps: %s",
+            restriction_str);
+
+        g_free (restriction_str);
+        return FALSE;
+      }
+      g_free (restriction_str);
+    } else if (!gst_structure_get (structure, "restrictions", GST_TYPE_CAPS,
+            &caps, NULL)) {
+      gchar *tmp = gst_structure_to_string (structure);
+
+      g_set_error (error, GES_ERROR, 0, "Can't use restrictions caps from %s",
+          tmp);
+
+      g_object_unref (track);
+      return FALSE;
+    }
+
+    ges_track_set_restriction_caps (track, caps);
+    gst_caps_unref (caps);
+  }
+
+  return ges_timeline_add_track (timeline, track);
+}
+
+gboolean
 _ges_container_add_child_from_struct (GESTimeline * timeline,
     GstStructure * structure, GError ** error)
 {
index 3a27aab..13efc16 100644 (file)
@@ -37,6 +37,11 @@ _ges_add_clip_from_struct                     (GESTimeline * timeline,
                                                GError ** error);
 
 G_GNUC_INTERNAL gboolean
+_ges_add_track_from_struct                    (GESTimeline * timeline,
+                                               GstStructure * structure,
+                                               GError ** error);
+
+G_GNUC_INTERNAL gboolean
 _ges_container_add_child_from_struct          (GESTimeline * timeline,
                                                GstStructure * structure,
                                                GError ** error);
index d442820..da8743f 100644 (file)
@@ -16,6 +16,7 @@ TEST_CLIP       [ ]+\+test-clip[ ]+
 TRANSITION      [ ]+\+transition[ ]+
 EFFECT          [ ]+\+effect[ ]+
 TITLE           [ ]+\+title[ ]+
+TRACK           [ ]+\+track[ ]+
 
 SETTER          [ ]+set-[^ ]+[ ]+
 
@@ -35,7 +36,7 @@ VALUE           {STRING}|([abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0
                ges_structure_parser_parse_string (yyextra, yytext, FALSE);
 }
 
-{CLIP}|{TRANSITION}|{EFFECT}|{TEST_CLIP}|{TITLE}   {
+{TRACK}|{CLIP}|{TRANSITION}|{EFFECT}|{TEST_CLIP}|{TITLE}   {
                ges_structure_parser_parse_symbol (yyextra, yytext);
 }
 
index f0a6de2..be33bd0 100644 (file)
@@ -716,6 +716,7 @@ static gboolean
 _create_timeline (GESLauncher * self, const gchar * serialized_timeline,
     const gchar * proj_uri, gboolean validate)
 {
+  GESLauncherParsedOptions *opts = &self->priv->parsed_options;
   GESProject *project;
 
   GError *error = NULL;
@@ -723,8 +724,37 @@ _create_timeline (GESLauncher * self, const gchar * serialized_timeline,
   if (proj_uri != NULL) {
     project = ges_project_new (proj_uri);
   } else if (!validate) {
-    GST_INFO ("serialized timeline is %s", serialized_timeline);
-    project = ges_project_new (serialized_timeline);
+    GString *timeline_str = g_string_new (serialized_timeline);
+
+    if (!strstr (serialized_timeline, "+track")) {
+      GString *track_def;
+
+      if (opts->track_types & GES_TRACK_TYPE_VIDEO) {
+        track_def = g_string_new (" +track video ");
+
+        if (opts->video_track_caps)
+          g_string_append_printf (track_def, " restrictions=[%s] ",
+              opts->video_track_caps);
+
+        g_string_prepend (timeline_str, track_def->str);
+        g_string_free (track_def, TRUE);
+      }
+
+      if (opts->track_types & GES_TRACK_TYPE_AUDIO) {
+        track_def = g_string_new (" +track audio ");
+
+        if (opts->audio_track_caps)
+          g_string_append_printf (track_def, " restrictions=[%s] ",
+              opts->audio_track_caps);
+
+        g_string_prepend (timeline_str, track_def->str);
+        g_string_free (track_def, TRUE);
+      }
+    }
+
+    GST_INFO ("Launching timeline: `%s`", timeline_str->str);
+    project = ges_project_new (timeline_str->str);
+    g_string_free (timeline_str, TRUE);
   } else {
     project = ges_project_new (NULL);
   }