1 /* GStreamer Editing Services
2 * Copyright (C) 2010 Edward Hervey <bilboed@bilboed.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
28 #include <glib/gprintf.h>
30 #include <gst/pbutils/encoding-profile.h>
32 #include <locale.h> /* for LC_ALL */
33 #include "ges-validate.h"
36 static guint repeat = 0;
37 static gboolean mute = FALSE;
38 static gboolean disable_mixing = FALSE;
39 static GESPipeline *pipeline = NULL;
40 static gboolean seenerrors = FALSE;
41 static gchar *save_path = NULL;
42 static GPtrArray *new_paths = NULL;
43 static GMainLoop *mainloop;
44 static GHashTable *tried_uris;
45 static GESTrackType track_types = GES_TRACK_TYPE_AUDIO | GES_TRACK_TYPE_VIDEO;
46 static GESTimeline *timeline;
49 ensure_uri (gchar * location)
51 if (gst_uri_is_valid (location))
52 return g_strdup (location);
54 return gst_filename_to_uri (location, NULL);
58 get_flags_from_string (GType type, const gchar * str_flags)
62 GFlagsClass *class = g_type_class_ref (type);
64 for (i = 0; i < class->n_values; i++) {
65 if (g_strrstr (str_flags, class->values[i].value_nick)) {
66 flags |= class->values[i].value;
69 g_type_class_unref (class);
76 _add_media_new_paths_recursing (const gchar * value)
79 GFileEnumerator *fenum;
80 GFile *file = g_file_new_for_uri (value);
82 if (!(fenum = g_file_enumerate_children (file,
83 "standard::*", G_FILE_QUERY_INFO_NONE, NULL, NULL))) {
84 GST_INFO ("%s is not a folder", value);
89 GST_INFO ("Adding folder: %s", value);
90 g_ptr_array_add (new_paths, g_strdup (value));
91 for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
92 info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
94 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
95 GFile *f = g_file_enumerator_get_child (fenum, info);
96 gchar *uri = g_file_get_uri (f);
98 _add_media_new_paths_recursing (uri);
105 gst_object_unref (file);
107 gst_object_unref (fenum);
111 _add_media_path (const gchar * option_name, const gchar * value,
112 gpointer udata, GError ** error)
114 g_return_val_if_fail (gst_uri_is_valid (value), FALSE);
116 if (g_strcmp0 (option_name, "--sample-path-recurse") == 0) {
117 _add_media_new_paths_recursing (value);
119 GST_INFO ("Adding folder: %s", value);
120 g_ptr_array_add (new_paths, g_strdup (value));
127 parse_track_type (const gchar * option_name, const gchar * value,
128 gpointer udata, GError ** error)
130 track_types = get_flags_from_string (GES_TYPE_TRACK_TYPE, value);
132 if (track_types == 0)
139 thumbnail_cb (gpointer pipeline)
142 GESPipeline *p = (GESPipeline *) pipeline;
146 filename = g_strdup_printf ("thumbnail%d.jpg", i++);
148 res = ges_pipeline_save_thumbnail (p, -1, -1,
149 (gchar *) "image/jpeg", filename, NULL);
157 source_moved_cb (GESProject * project, GError * error, GESAsset * asset)
160 const gchar *old_uri = ges_asset_get_id (asset);
162 for (i = 0; i < new_paths->len; i++) {
163 gchar *basename, *res;
165 basename = g_path_get_basename (old_uri);
166 res = g_build_filename (new_paths->pdata[i], basename, NULL);
169 if (g_hash_table_lookup (tried_uris, res)) {
170 GST_DEBUG ("File already tried: %s\n", res);
173 g_hash_table_add (tried_uris, g_strdup (res));
182 error_loading_asset_cb (GESProject * project, GError * error,
183 const gchar * failed_id, GType extractable_type)
185 g_printerr ("Error loading asset %s: %s\n", failed_id, error->message);
188 g_main_loop_quit (mainloop);
192 project_loaded_cb (GESProject * project, GESTimeline * timeline)
194 GST_INFO ("Project loaded, playing it");
198 GError *error = NULL;
200 if (g_strcmp0 (save_path, "+r") == 0) {
201 uri = ges_project_get_uri (project);
202 } else if (!(uri = ensure_uri (save_path))) {
203 g_error ("couldn't create uri for '%s", save_path);
206 g_main_loop_quit (mainloop);
209 g_print ("\nSaving project to %s\n", uri);
210 ges_project_save (project, timeline, uri, NULL, TRUE, &error);
213 g_assert_no_error (error);
216 g_main_loop_quit (mainloop);
220 if (gst_element_set_state (GST_ELEMENT (pipeline),
221 GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
222 g_error ("Failed to start the pipeline\n");
227 check_time (char *time)
229 static GRegex *re = NULL;
232 if (NULL == (re = g_regex_new ("^[0-9]+(.[0-9]+)?$", G_REGEX_EXTENDED, 0,
237 if (g_regex_match (re, time, 0, NULL))
243 str_to_time (char *time)
247 g_return_val_if_fail (check_time (time), 0);
249 nsecs = g_ascii_strtod (time, NULL);
251 return nsecs * GST_SECOND;
255 create_timeline (int nbargs, gchar ** argv, const gchar * proj_uri, const gchar *scenario)
257 GESLayer *layer = NULL;
258 GESTrack *tracka = NULL, *trackv = NULL;
259 GESTimeline *timeline;
260 gboolean activate_before_paused = TRUE;
262 GESProject *project = ges_project_new (proj_uri);
265 g_signal_connect (project, "missing-uri",
266 G_CALLBACK (source_moved_cb), NULL);
268 g_signal_connect (project, "error-loading-asset",
269 G_CALLBACK (error_loading_asset_cb), NULL);
271 if (proj_uri != NULL) {
272 g_signal_connect (project, "loaded", G_CALLBACK (project_loaded_cb), NULL);
275 timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
278 activate_before_paused = FALSE;
279 goto activate_validate;
282 if (track_types & GES_TRACK_TYPE_AUDIO) {
283 tracka = GES_TRACK (ges_audio_track_new ());
285 ges_track_set_mixing (tracka, FALSE);
287 if (!(ges_timeline_add_track (timeline, tracka)))
291 if (track_types & GES_TRACK_TYPE_VIDEO) {
292 trackv = GES_TRACK (ges_video_track_new ());
295 ges_track_set_mixing (trackv, FALSE);
297 if (!(ges_timeline_add_track (timeline, trackv)))
301 /* Here we've finished initializing our timeline, we're
302 * ready to start using it... by solely working with the layer !*/
304 for (i = 0; i < nbargs / 3; i++) {
307 char *source = argv[i * 3];
308 char *arg0 = argv[(i * 3) + 1];
309 guint64 duration = str_to_time (argv[(i * 3) + 2]);
312 /* We are only going to be doing one layer of clips */
313 layer = (GESLayer *) ges_layer_new ();
314 activate_before_paused = FALSE;
316 /* Add the tracks and the layer to the timeline */
317 if (!ges_timeline_add_layer (timeline, layer))
322 duration = GST_CLOCK_TIME_NONE;
324 if (!g_strcmp0 ("+pattern", source)) {
325 clip = GES_CLIP (ges_test_clip_new_for_nick (arg0));
327 g_error ("%s is an invalid pattern name!\n", arg0);
331 g_object_set (G_OBJECT (clip), "duration", duration, NULL);
333 g_printf ("Adding <pattern:%s> duration %" GST_TIME_FORMAT "\n", arg0,
334 GST_TIME_ARGS (duration));
337 else if (!g_strcmp0 ("+transition", source)) {
338 clip = GES_CLIP (ges_transition_clip_new_for_nick (arg0));
341 g_error ("invalid transition type\n");
345 g_object_set (G_OBJECT (clip), "duration", duration, NULL);
347 g_printf ("Adding <transition:%s> duration %" GST_TIME_FORMAT "\n", arg0,
348 GST_TIME_ARGS (duration));
352 else if (!g_strcmp0 ("+title", source)) {
353 clip = GES_CLIP (ges_title_clip_new ());
355 g_object_set (clip, "duration", duration, "text", arg0, NULL);
357 g_printf ("Adding <title:%s> duration %" GST_TIME_FORMAT "\n", arg0,
358 GST_TIME_ARGS (duration));
366 GError *error = NULL;
368 if (!(uri = ensure_uri (source))) {
369 GST_ERROR ("couldn't create uri for '%s'", source);
373 inpoint = str_to_time (argv[i * 3 + 1]);
374 asset = GES_ASSET (ges_uri_clip_asset_request_sync (uri, &error));
376 g_printerr ("Can not create asset for %s", uri);
381 ges_project_add_asset (project, asset);
382 clip = GES_CLIP (ges_asset_extract (asset, &error));
384 g_printerr ("Can not extract asset for %s", uri);
389 if (!GST_CLOCK_TIME_IS_VALID (duration))
391 GES_TIMELINE_ELEMENT_DURATION (clip) - (GstClockTime) inpoint;
394 "in-point", (guint64) inpoint, "duration", (guint64) duration, NULL);
396 g_printf ("Adding clip %s inpoint:%" GST_TIME_FORMAT " duration:%"
397 GST_TIME_FORMAT "\n", uri, GST_TIME_ARGS (inpoint),
398 GST_TIME_ARGS (duration));
403 g_object_set (G_OBJECT (clip), "start", ges_layer_get_duration (layer),
405 ges_layer_add_clip (layer, clip);
409 if (ges_validate_activate (GST_PIPELINE (pipeline), scenario, activate_before_paused) == FALSE) {
410 g_error ("Could not activate scenario %s", scenario);
417 gst_object_unref (timeline);
423 create_pipeline (GESTimeline ** ret_timeline, gchar * load_path,
424 int argc, char **argv, const gchar *scenario)
427 GESTimeline *timeline = NULL;
429 /* Timeline creation */
431 g_printf ("Loading project from : %s\n", load_path);
433 if (!(uri = ensure_uri (load_path))) {
434 g_error ("couldn't create uri for '%s'", load_path);
439 pipeline = ges_pipeline_new ();
441 if (!(timeline = create_timeline (argc, argv, uri, scenario)))
444 ges_timeline_commit (timeline);
449 /* save project if path is given. we do this now in case GES crashes or
450 * hangs during playback. */
451 if (save_path && !load_path) {
453 if (!(uri = ensure_uri (save_path))) {
454 g_error ("couldn't create uri for '%s", save_path);
457 ges_timeline_save_to_uri (timeline, uri, NULL, TRUE, NULL);
461 /* In order to view our timeline, let's grab a convenience pipeline to put
462 * our timeline in. */
465 GstElement *sink = gst_element_factory_make ("fakesink", NULL);
467 g_object_set (sink, "sync", TRUE, NULL);
468 ges_pipeline_preview_set_audio_sink (pipeline, sink);
470 sink = gst_element_factory_make ("fakesink", NULL);
471 g_object_set (sink, "sync", TRUE, NULL);
472 ges_pipeline_preview_set_video_sink (pipeline, sink);
475 /* Add the timeline to that pipeline */
476 if (!ges_pipeline_set_timeline (pipeline, timeline))
479 *ret_timeline = timeline;
487 gst_object_unref (timeline);
489 gst_object_unref (pipeline);
495 bus_message_cb (GstBus * bus, GstMessage * message, GMainLoop * mainloop)
497 switch (GST_MESSAGE_TYPE (message)) {
498 case GST_MESSAGE_WARNING:{
499 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
500 GST_DEBUG_GRAPH_SHOW_ALL, "ges-launch.warning");
503 case GST_MESSAGE_ERROR:{
505 gchar *dbg_info = NULL;
507 gst_message_parse_error (message, &err, &dbg_info);
508 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
509 GST_DEBUG_GRAPH_SHOW_ALL, "ges-launch-error");
510 g_printerr ("ERROR from element %s: %s\n", GST_OBJECT_NAME (message->src),
512 g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
516 g_main_loop_quit (mainloop);
519 case GST_MESSAGE_EOS:
521 g_printerr ("Looping again\n");
522 if (!gst_element_seek_simple (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
523 GST_SEEK_FLAG_FLUSH, 0))
524 g_printerr ("seeking failed\n");
526 g_printerr ("seeking succeeded\n");
527 gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
528 g_printerr ("Looping set\n");
531 g_printerr ("\nDone\n");
532 g_main_loop_quit (mainloop);
535 case GST_MESSAGE_STATE_CHANGED:
536 if (GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipeline)) {
538 GstState old, new, pending;
539 gchar *state_transition_name;
541 gst_message_parse_state_changed (message, &old, &new, &pending);
542 state_transition_name = g_strdup_printf ("%s_%s",
543 gst_element_state_get_name (old), gst_element_state_get_name (new));
544 dump_name = g_strconcat ("ges-launch.", state_transition_name, NULL);
547 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
548 GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
551 g_free (state_transition_name);
560 print_enum (GType enum_type)
562 GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (enum_type));
565 for (i = 0; i < enum_class->n_values; i++) {
566 g_printf ("%s\n", enum_class->values[i].value_nick);
569 g_type_class_unref (enum_class);
573 print_transition_list (void)
575 print_enum (GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE);
579 print_pattern_list (void)
581 print_enum (GES_VIDEO_TEST_PATTERN_TYPE);
584 static GstEncodingProfile *
585 _parse_encoding_profile (const gchar * format)
588 char *preset_name = NULL;
589 GstEncodingProfile *encoding_profile;
590 gchar **restriction_format, **preset_v;
592 guint i, presence = 0;
593 GstCaps *restrictioncaps = NULL;
594 gchar **strpresence_v, **strcaps_v = g_strsplit (format, ":", 0);
596 if (strcaps_v[0] && *strcaps_v[0]) {
597 caps = gst_caps_from_string (strcaps_v[0]);
599 g_printerr ("Could not parse caps %s", strcaps_v[0]);
603 GST_ENCODING_PROFILE (gst_encoding_container_profile_new
604 ("User profile", "User profile", caps, NULL));
605 gst_caps_unref (caps);
607 encoding_profile = NULL;
610 for (i = 1; strcaps_v[i]; i++) {
611 GstEncodingProfile *profile = NULL;
612 gchar *strcaps, *strpresence;
614 restriction_format = g_strsplit (strcaps_v[i], "->", 0);
615 if (restriction_format[1]) {
616 restrictioncaps = gst_caps_from_string (restriction_format[0]);
617 strcaps = g_strdup (restriction_format[1]);
619 restrictioncaps = NULL;
620 strcaps = g_strdup (restriction_format[0]);
622 g_strfreev (restriction_format);
624 preset_v = g_strsplit (strcaps, "+", 0);
626 strpresence = preset_v[1];
628 strcaps = g_strdup (preset_v[0]);
630 strpresence = preset_v[0];
633 strpresence_v = g_strsplit (strpresence, "|", 0);
634 if (strpresence_v[1]) { /* We have a presence */
637 if (preset_v[1]) { /* We have preset and presence */
638 preset_name = g_strdup (strpresence_v[0]);
639 } else { /* We have a presence but no preset */
641 strcaps = g_strdup (strpresence_v[0]);
644 presence = strtoll (strpresence_v[1], &endptr, 10);
645 if (endptr == strpresence_v[1]) {
646 g_printerr ("Wrong presence %s\n", strpresence_v[1]);
650 } else { /* We have no presence */
651 if (preset_v[1]) { /* Not presence but preset */
652 preset_name = g_strdup (preset_v[1]);
654 strcaps = g_strdup (preset_v[0]);
655 } /* Else we have no presence nor preset */
657 g_strfreev (strpresence_v);
658 g_strfreev (preset_v);
660 GST_DEBUG ("Creating preset with restrictions: %" GST_PTR_FORMAT
661 ", caps: %s, preset %s, presence %d", restrictioncaps, strcaps,
662 preset_name ? preset_name : "none", presence);
664 caps = gst_caps_from_string (strcaps);
667 g_warning ("Could not create caps for %s", strcaps_v[i]);
672 if (g_str_has_prefix (strcaps_v[i], "audio/")) {
673 profile = GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps,
674 preset_name, restrictioncaps, presence));
675 } else if (g_str_has_prefix (strcaps_v[i], "video/") ||
676 g_str_has_prefix (strcaps_v[i], "image/")) {
677 profile = GST_ENCODING_PROFILE (gst_encoding_video_profile_new (caps,
678 preset_name, restrictioncaps, presence));
681 g_free (preset_name);
682 gst_caps_unref (caps);
684 gst_caps_unref (restrictioncaps);
686 if (profile == NULL) {
687 g_warning ("No way to create a preset for caps: %s", strcaps_v[i]);
692 if (encoding_profile) {
693 if (gst_encoding_container_profile_add_profile
694 (GST_ENCODING_CONTAINER_PROFILE (encoding_profile),
696 g_warning ("Can not create a preset for caps: %s", strcaps_v[i]);
701 encoding_profile = profile;
704 g_strfreev (strcaps_v);
706 return encoding_profile;
710 main (int argc, gchar ** argv)
714 gchar *outputuri = NULL;
715 const gchar *format = NULL;
716 gchar *exclude_args = NULL;
717 static gboolean smartrender = FALSE;
718 static gboolean list_transitions = FALSE;
719 static gboolean list_patterns = FALSE;
720 static gdouble thumbinterval = 0;
721 static gboolean verbose = FALSE;
722 gchar *load_path = NULL;
723 const gchar *scenario = NULL;
724 GOptionEntry options[] = {
725 {"thumbnail", 'm', 0.0, G_OPTION_ARG_DOUBLE, &thumbinterval,
726 "Take thumbnails every n seconds (saved in current directory)", "N"},
727 {"smartrender", 's', 0, G_OPTION_ARG_NONE, &smartrender,
728 "Render to outputuri, and avoid decoding/reencoding", NULL},
729 {"outputuri", 'o', 0, G_OPTION_ARG_STRING, &outputuri,
730 "URI to encode to", "URI (<protocol>://<location>)"},
731 {"format", 'f', 0, G_OPTION_ARG_STRING, &format,
732 "Set the properties to use for the encoding profile "
733 "(in case of transcoding.) For example:\n"
734 "video/mpegts:video/x-raw,width=1920,height=1080->video/x-h264:audio/x-ac3\n"
735 "A preset name can be used by adding +presetname, eg:\n"
736 "video/webm:video/x-vp8+mypreset:audio/x-vorbis\n"
737 "The presence property of the profile can be specified with |<presence>, eg:\n"
738 "video/webm:video/x-vp8|<presence>:audio/x-vorbis\n",
739 "properties-values"},
740 {"repeat", 'r', 0, G_OPTION_ARG_INT, &repeat,
741 "Number of time to repeat timeline", NULL},
742 {"list-transitions", 't', 0, G_OPTION_ARG_NONE, &list_transitions,
743 "List valid transition types and exit", NULL},
744 {"list-patterns", 'p', 0, G_OPTION_ARG_NONE, &list_patterns,
745 "List patterns and exit", NULL},
746 {"save", 'z', 0, G_OPTION_ARG_STRING, &save_path,
747 "Save project to file before rendering", "<path>"},
748 {"load", 'l', 0, G_OPTION_ARG_STRING, &load_path,
749 "Load project from file before rendering", "<path>"},
750 {"verbose", 0, 0, G_OPTION_ARG_NONE, &verbose,
751 "Output status information and property notifications", NULL},
752 {"exclude", 'X', 0, G_OPTION_ARG_NONE, &exclude_args,
753 "Do not output status information of TYPE", "TYPE1,TYPE2,..."},
754 {"sample-paths", 'P', 0, G_OPTION_ARG_CALLBACK, &_add_media_path,
755 "List of pathes to look assets in if they were moved"},
756 {"sample-path-recurse", 'R', 0, G_OPTION_ARG_CALLBACK,
758 "Same as --sample-paths but recursing into the folder"},
759 {"track-types", 'p', 0, G_OPTION_ARG_CALLBACK, &parse_track_type,
760 "Defines the track types to be created"},
761 {"mute", 0, 0, G_OPTION_ARG_NONE, &mute,
762 "Mute playback output, which means that we use faksinks"},
763 {"disable-mixing", 0, 0, G_OPTION_ARG_NONE, &disable_mixing,
764 "Do not use mixing element in the tracks"},
765 #ifdef HAVE_GST_VALIDATE
766 {"set-scenario", 0, 0, G_OPTION_ARG_STRING, &scenario,
767 "Specify a GstValidate scenario to run, 'none' means load gst-validate"
768 " but run no scenario on it", "<scenario_name>"},
776 setlocale (LC_ALL, "");
778 new_paths = g_ptr_array_new_with_free_func (g_free);
779 ctx = g_option_context_new ("- plays or renders a timeline.");
780 g_option_context_set_summary (ctx,
781 "ges-launch renders a timeline, which can be specified on the commandline,\n"
782 "or loaded from a file using the -q option.\n\n"
783 "A timeline is a list of files, patterns, and transitions to be rendered\n"
784 "one after the other. Files and Patterns provide video and audio as the\n"
785 "primary input, and transitions animate between the end of one file/pattern\n"
786 "and the beginning of a new one. Hence, transitions can only be listed\n"
787 "in between patterns or files.\n\n"
788 "A file is a triplet of filename, inpoint (in seconds) and\n"
789 "duration (in seconds). If the duration is 0, the full file length is used.\n\n"
790 "Patterns and transitions are triplets that begin with either \"+pattern\"\n"
791 "or \"+transition\", followed by a <type> and duration (in seconds, must be\n"
792 "greater than 0)\n\n"
793 "Durations in all cases can be fractions of a second.\n\n"
795 "ges-launch file1.avi 0 45 +transition crossfade 3.5 file2.avi 0 0");
796 g_option_context_add_main_entries (ctx, options, NULL);
797 g_option_context_add_group (ctx, gst_init_get_option_group ());
799 if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
800 g_printerr ("Error initializing: %s\n", err->message);
801 g_option_context_free (ctx);
805 /* Initialize the GStreamer Editing Services */
807 g_printerr ("Error initializing GES\n");
812 if (list_transitions) {
813 print_transition_list ();
818 print_pattern_list ();
822 tried_uris = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
823 if (((!load_path && !scenario && (argc < 4)))) {
824 g_printf ("%s", g_option_context_get_help (ctx, TRUE, NULL));
825 g_option_context_free (ctx);
829 g_option_context_free (ctx);
831 /* Create the pipeline */
832 create_pipeline (&timeline, load_path, argc - 1, argv + 1, scenario);
836 /* Setup profile/encoding if needed */
837 if (smartrender || outputuri) {
838 GstEncodingProfile *prof = NULL;
842 GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));
843 const GList *profiles = ges_project_list_encoding_profiles (proj);
845 prof = profiles ? profiles->data : NULL;
850 format = "application/ogg:video/x-theora:audio/x-vorbis";
852 prof = _parse_encoding_profile (format);
856 outputuri = ensure_uri (outputuri);
858 if (!prof || !ges_pipeline_set_render_settings (pipeline, outputuri, prof)
859 || !ges_pipeline_set_mode (pipeline,
860 smartrender ? GES_PIPELINE_MODE_SMART_RENDER :
861 GES_PIPELINE_MODE_RENDER)) {
867 gst_encoding_profile_unref (prof);
869 ges_pipeline_set_mode (pipeline, GES_PIPELINE_MODE_PREVIEW);
873 gchar **exclude_list =
874 exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL;
875 g_signal_connect (pipeline, "deep-notify",
876 G_CALLBACK (gst_object_default_deep_notify), exclude_list);
879 /* Play the pipeline */
880 mainloop = g_main_loop_new (NULL, FALSE);
882 if (thumbinterval != 0.0) {
883 g_printf ("thumbnailing every %f seconds\n", thumbinterval);
884 g_timeout_add (1000 * thumbinterval, thumbnail_cb, pipeline);
887 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
888 gst_bus_add_signal_watch (bus);
889 g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), mainloop);
891 if (load_path == NULL && gst_element_set_state (GST_ELEMENT (pipeline),
892 GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
893 g_error ("Failed to start the pipeline\n");
896 g_main_loop_run (mainloop);
898 gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
900 validate_res = ges_validate_clean (GST_PIPELINE (pipeline));
901 if (seenerrors == FALSE)
902 seenerrors = validate_res;
904 g_hash_table_unref (tried_uris);
905 g_ptr_array_unref (new_paths);
907 return (int) seenerrors;