command-line-formatter: Add track management to timeline description

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>
This commit is contained in:
Thibault Saunier 2021-01-12 15:55:52 -03:00
parent e3a30744e8
commit 6c5daf8c81
6 changed files with 143 additions and 20 deletions

View 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;
@ -441,6 +455,16 @@ _ges_command_line_formatter_add_title_clip (GESTimeline * timeline,
return _ges_add_clip_from_struct (timeline, structure, error);
}
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 !*/

View 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

View file

@ -569,6 +569,69 @@ beach:
return res;
}
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)

View file

@ -36,6 +36,11 @@ _ges_add_clip_from_struct (GESTimeline * timeline,
GstStructure * structure,
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,

View 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);
}

View 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);
}