ges-launch.c 20.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* GStreamer Editing Services
 * Copyright (C) 2010 Edward Hervey <bilboed@bilboed.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
16
17
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
18
19
20
21
22
23
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

Thiago Santos's avatar
Thiago Santos committed
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <glib.h>
28
#include <glib/gprintf.h>
29
#include <ges/ges.h>
30
#include <gst/pbutils/encoding-profile.h>
31

32
33
#include <locale.h>             /* for LC_ALL */

34
35
/* GLOBAL VARIABLE */
static guint repeat = 0;
36
static GESPipeline *pipeline = NULL;
37
static gboolean seenerrors = FALSE;
38
39
static gchar **new_paths = NULL;
static GMainLoop *mainloop;
40
static GHashTable *tried_uris;
41

42
43
44
45
46
static gchar *
ensure_uri (gchar * location)
{
  if (gst_uri_is_valid (location))
    return g_strdup (location);
47
48
  else
    return gst_filename_to_uri (location, NULL);
49
50
}

51
static gboolean
52
53
54
thumbnail_cb (gpointer pipeline)
{
  static int i = 0;
55
  GESPipeline *p = (GESPipeline *) pipeline;
56
  gchar *filename;
Edward Hervey's avatar
Edward Hervey committed
57
  gboolean res;
58
59
60

  filename = g_strdup_printf ("thumbnail%d.jpg", i++);

61
  res = ges_pipeline_save_thumbnail (p, -1, -1,
62
      (gchar *) "image/jpeg", filename, NULL);
Edward Hervey's avatar
Edward Hervey committed
63
64
65
66

  g_free (filename);

  return res;
67
}
68

69
70
71
72
73
74
75
76
77
78
79
80
81
static gchar *
source_moved_cb (GESProject * project, GError * error, GESAsset * asset)
{
  gint i;
  const gchar *old_uri = ges_asset_get_id (asset);

  for (i = 0; new_paths[i] != NULL; i++) {
    gchar *basename, *res;

    basename = g_path_get_basename (old_uri);
    res = g_build_filename (new_paths[i], basename, NULL);
    g_free (basename);

82
83
84
85
86
87
88
    if (g_hash_table_lookup (tried_uris, res)) {
      GST_DEBUG ("File already tried: %s\n", res);
      g_free (res);
    } else {
      g_hash_table_add (tried_uris, g_strdup (res));
      return res;
    }
89
90
91
92
93
94
95
96
97
  }

  return NULL;
}

static void
error_loading_asset_cb (GESProject * project, GError * error,
    const gchar * failed_id, GType extractable_type)
{
98
  g_printerr ("Error loading asset %s: %s\n", failed_id, error->message);
99
100
101
102

  g_main_loop_quit (mainloop);
}

103
static gboolean
104
105
check_time (char *time)
{
106
  static GRegex *re = NULL;
107

108
109
  if (!re) {
    if (NULL == (re = g_regex_new ("^[0-9]+(.[0-9]+)?$", G_REGEX_EXTENDED, 0,
110
                NULL)))
111
      return FALSE;
112
113
  }

114
  if (g_regex_match (re, time, 0, NULL))
115
116
117
118
    return TRUE;
  return FALSE;
}

119
static guint64
120
121
122
123
124
str_to_time (char *time)
{
  if (check_time (time)) {
    return (guint64) (atof (time) * GST_SECOND);
  }
125
  GST_ERROR ("%s not a valid time", time);
126
127
128
  return 0;
}

129
static GESTimeline *
130
create_timeline (int nbargs, gchar ** argv, const gchar * proj_uri)
131
{
132
  GESLayer *layer;
133
  GESTrack *tracka = NULL, *trackv = NULL;
134
135
  GESTimeline *timeline;
  guint i;
136
137
138
139
140
141
142
143
  GESProject *project = ges_project_new (proj_uri);

  if (new_paths)
    g_signal_connect (project, "missing-uri",
        G_CALLBACK (source_moved_cb), NULL);

  g_signal_connect (project, "error-loading-asset",
      G_CALLBACK (error_loading_asset_cb), NULL);
144

145
  timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
146

147
148
149
  if (proj_uri)
    return timeline;

150
151
  tracka = GES_TRACK (ges_audio_track_new ());
  trackv = GES_TRACK (ges_video_track_new ());
152

153
  /* We are only going to be doing one layer of clips */
154
  layer = (GESLayer *) ges_simple_layer_new ();
155
156
157

  /* Add the tracks and the layer to the timeline */
  if (!ges_timeline_add_layer (timeline, layer) ||
158
159
      !(ges_timeline_add_track (timeline, tracka)) ||
      !(ges_timeline_add_track (timeline, trackv)))
160
    goto build_failure;
161

162
  /* Here we've finished initializing our timeline, we're
163
164
165
   * ready to start using it... by solely working with the layer !*/

  for (i = 0; i < nbargs / 3; i++) {
166
    GESClip *clip;
167

168
169
    char *source = argv[i * 3];
    char *arg0 = argv[(i * 3) + 1];
170
    guint64 duration = str_to_time (argv[(i * 3) + 2]);
171
172

    if (!g_strcmp0 ("+pattern", source)) {
173
174
      clip = GES_CLIP (ges_test_clip_new_for_nick (arg0));
      if (!clip) {
175
176
177
        g_error ("%s is an invalid pattern name!\n", arg0);
        goto build_failure;
      }
178

179
      g_object_set (G_OBJECT (clip), "duration", duration, NULL);
180

181
182
      g_printf ("Adding <pattern:%s> duration %" GST_TIME_FORMAT "\n", arg0,
          GST_TIME_ARGS (duration));
183
184
    }

185
    else if (!g_strcmp0 ("+transition", source)) {
186
187
188
189
190
      if (duration <= 0) {
        g_error ("durations must be greater than 0");
        goto build_failure;
      }

191
      clip = GES_CLIP (ges_transition_clip_new_for_nick (arg0));
192

193
      if (!clip) {
194
        g_error ("invalid transition type\n");
195
196
        goto build_failure;
      }
197

198
      g_object_set (G_OBJECT (clip), "duration", duration, NULL);
199

200
201
      g_printf ("Adding <transition:%s> duration %" GST_TIME_FORMAT "\n", arg0,
          GST_TIME_ARGS (duration));
202
203
204

    }

205
    else if (!g_strcmp0 ("+title", source)) {
206
      clip = GES_CLIP (ges_title_clip_new ());
207

208
      g_object_set (clip, "duration", duration, "text", arg0, NULL);
209

210
211
      g_printf ("Adding <title:%s> duration %" GST_TIME_FORMAT "\n", arg0,
          GST_TIME_ARGS (duration));
212
213
    }

214
    else {
Brandon Lewis's avatar
Brandon Lewis committed
215
      gchar *uri;
216
      GESAsset *asset;
Brandon Lewis's avatar
Brandon Lewis committed
217
218
      guint64 inpoint;

219
220
      GError *error = NULL;

221
      if (!(uri = ensure_uri (source))) {
222
        GST_ERROR ("couldn't create uri for '%s'", source);
223
        goto build_failure;
224
      }
Brandon Lewis's avatar
Brandon Lewis committed
225

226
      inpoint = str_to_time (argv[i * 3 + 1]);
227
228
229
230
231
232
233
      asset = GES_ASSET (ges_uri_clip_asset_request_sync (uri, &error));
      if (error) {
        g_printerr ("Can not create asset for %s", uri);

        return NULL;
      }

234
      ges_project_add_asset (project, asset);
235
236
237
238
239
240
241
      clip = GES_CLIP (ges_asset_extract (asset, &error));
      if (error) {
        g_printerr ("Can not extract asset for %s", uri);

        return NULL;
      }

242
      g_object_set (clip,
243
244
          "in-point", (guint64) inpoint, "duration", (guint64) duration, NULL);

245
      g_printf ("Adding clip %s inpoint:%" GST_TIME_FORMAT " duration:%"
246
247
248
          GST_TIME_FORMAT "\n", uri, GST_TIME_ARGS (inpoint),
          GST_TIME_ARGS (duration));

249
250
      g_free (uri);
    }
251

252
    /* Since we're using a GESSimpleLayer, objects will be automatically
253
     * appended to the end of the layer */
254
    ges_layer_add_clip (layer, clip);
255
256
  }

257
  return timeline;
258
259
260

build_failure:
  {
261
    gst_object_unref (timeline);
262
263
    return NULL;
  }
264
265
}

266
static GESPipeline *
267
create_pipeline (GESTimeline ** ret_timeline, gchar * load_path,
268
    gchar * save_path, int argc, char **argv)
269
{
270
  gchar *uri = NULL;
271
  GESPipeline *pipeline = NULL;
272
  GESTimeline *timeline = NULL;
273

274
  /* Timeline creation */
275
  if (load_path) {
276
    g_printf ("Loading project from : %s\n", load_path);
277
278

    if (!(uri = ensure_uri (load_path))) {
279
280
      g_error ("couldn't create uri for '%s'", load_path);
      goto failure;
281
    }
282
  }
283
284
285
  if (!(timeline = create_timeline (argc, argv, uri)))
    goto failure;

286
  ges_timeline_commit (timeline);
287

288
289
290
  if (uri)
    g_free (uri);

291
292
293
294
295
296
297
  /* save project if path is given. we do this now in case GES crashes or
   * hangs during playback. */
  if (save_path) {
    gchar *uri;
    if (!(uri = ensure_uri (save_path))) {
      g_error ("couldn't create uri for '%s", save_path);
      goto failure;
298
    }
299
    ges_timeline_save_to_uri (timeline, uri, NULL, TRUE, NULL);
300
    g_free (uri);
301
302
  }

303
304
  /* In order to view our timeline, let's grab a convenience pipeline to put
   * our timeline in. */
305
  pipeline = ges_pipeline_new ();
306
307

  /* Add the timeline to that pipeline */
308
  if (!ges_pipeline_add_timeline (pipeline, timeline))
309
    goto failure;
310

311
  *ret_timeline = timeline;
312
  return pipeline;
313
314
315

failure:
  {
316
317
    if (uri)
      g_free (uri);
318
    if (timeline)
319
      gst_object_unref (timeline);
320
    if (pipeline)
321
      gst_object_unref (pipeline);
322
323
    return NULL;
  }
324
325
326
327
328
329
}

static void
bus_message_cb (GstBus * bus, GstMessage * message, GMainLoop * mainloop)
{
  switch (GST_MESSAGE_TYPE (message)) {
330
331
332
333
334
    case GST_MESSAGE_ERROR:{
      GError *err = NULL;
      gchar *dbg_info = NULL;

      gst_message_parse_error (message, &err, &dbg_info);
335
336
337
338
      GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
          GST_DEBUG_GRAPH_SHOW_ALL, "ges-launch-error");
      g_printerr ("ERROR from element %s: %s\n", GST_OBJECT_NAME (message->src),
          err->message);
339
340
341
      g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
      g_error_free (err);
      g_free (dbg_info);
342
      seenerrors = TRUE;
343
344
      g_main_loop_quit (mainloop);
      break;
345
    }
346
    case GST_MESSAGE_EOS:
347
      if (repeat > 0) {
348
349
350
351
352
353
        g_printerr ("Looping again\n");
        if (!gst_element_seek_simple (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
                GST_SEEK_FLAG_FLUSH, 0))
          g_printerr ("seeking failed\n");
        else
          g_printerr ("seeking succeeded\n");
354
        gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
355
        g_printerr ("Looping set\n");
356
357
        repeat -= 1;
      } else {
358
        g_printerr ("Done\n");
359
360
        g_main_loop_quit (mainloop);
      }
361
362
363
364
365
366
      break;
    default:
      break;
  }
}

367
368
static void
print_enum (GType enum_type)
369
{
370
371
  GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (enum_type));
  guint i;
372

373
  GST_ERROR ("%d", enum_class->n_values);
374

375
  for (i = 0; i < enum_class->n_values; i++) {
376
    g_printf ("%s\n", enum_class->values[i].value_nick);
377
378
  }

379
  g_type_class_unref (enum_class);
380
381
}

382
383
static void
print_transition_list (void)
384
{
385
386
  print_enum (GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE);
}
387

388
389
390
391
static void
print_pattern_list (void)
{
  print_enum (GES_VIDEO_TEST_PATTERN_TYPE);
392
393
}

394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
static gboolean
_print_position (void)
{
  gint64 position, duration;

  if (pipeline) {
    gst_element_query_position (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
        &position);
    gst_element_query_duration (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
        &duration);
    g_print ("<Position: %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT "/>\r",
        GST_TIME_ARGS (position), GST_TIME_ARGS (duration));
  }

  return TRUE;
}

411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
static GstEncodingProfile *
_parse_encoding_profile (const gchar * format)
{
  GstCaps *caps;
  char *preset_name = NULL;
  GstEncodingProfile *encoding_profile;
  gchar **restriction_format, **preset_v;

  guint i, presence = 0;
  GstCaps *restrictioncaps = NULL;
  gchar **strpresence_v, **strcaps_v = g_strsplit (format, ":", 0);

  if (strcaps_v[0] && *strcaps_v[0]) {
    caps = gst_caps_from_string (strcaps_v[0]);
    if (caps == NULL) {
      g_printerr ("Could not parse caps %s", strcaps_v[0]);
      return FALSE;
    }
    encoding_profile =
        GST_ENCODING_PROFILE (gst_encoding_container_profile_new
        ("User profile", "User profile", caps, NULL));
    gst_caps_unref (caps);
  } else {
    encoding_profile = NULL;
  }

  for (i = 1; strcaps_v[i]; i++) {
    GstEncodingProfile *profile = NULL;
    gchar *strcaps, *strpresence;

    restriction_format = g_strsplit (strcaps_v[i], "->", 0);
    if (restriction_format[1]) {
      restrictioncaps = gst_caps_from_string (restriction_format[0]);
      strcaps = g_strdup (restriction_format[1]);
    } else {
      restrictioncaps = NULL;
      strcaps = g_strdup (restriction_format[0]);
    }
    g_strfreev (restriction_format);

    preset_v = g_strsplit (strcaps, "+", 0);
    if (preset_v[1]) {
      strpresence = preset_v[1];
      g_free (strcaps);
      strcaps = g_strdup (preset_v[0]);
    } else {
      strpresence = preset_v[0];
    }

    strpresence_v = g_strsplit (strpresence, "|", 0);
    if (strpresence_v[1]) {     /* We have a presence */
      gchar *endptr;

      if (preset_v[1]) {        /* We have preset and presence */
        preset_name = g_strdup (strpresence_v[0]);
      } else {                  /* We have a presence but no preset */
        g_free (strcaps);
        strcaps = g_strdup (strpresence_v[0]);
      }

      presence = strtoll (strpresence_v[1], &endptr, 10);
      if (endptr == strpresence_v[1]) {
        g_printerr ("Wrong presence %s\n", strpresence_v[1]);

        return FALSE;
      }
    } else {                    /* We have no presence */
      if (preset_v[1]) {        /* Not presence but preset */
        preset_name = g_strdup (preset_v[1]);
        g_free (strcaps);
        strcaps = g_strdup (preset_v[0]);
      }                         /* Else we have no presence nor preset */
    }
    g_strfreev (strpresence_v);
    g_strfreev (preset_v);

    GST_DEBUG ("Creating preset with restrictions: %" GST_PTR_FORMAT
        ", caps: %s, preset %s, presence %d", restrictioncaps, strcaps,
        preset_name ? preset_name : "none", presence);

    caps = gst_caps_from_string (strcaps);
    g_free (strcaps);
    if (caps == NULL) {
      g_warning ("Could not create caps for %s", strcaps_v[i]);

      return FALSE;
    }

    if (g_str_has_prefix (strcaps_v[i], "audio/")) {
      profile = GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps,
              preset_name, restrictioncaps, presence));
    } else if (g_str_has_prefix (strcaps_v[i], "video/") ||
        g_str_has_prefix (strcaps_v[i], "image/")) {
      profile = GST_ENCODING_PROFILE (gst_encoding_video_profile_new (caps,
              preset_name, restrictioncaps, presence));
    }

    g_free (preset_name);
    gst_caps_unref (caps);
    if (restrictioncaps)
      gst_caps_unref (restrictioncaps);

    if (profile == NULL) {
      g_warning ("No way to create a preset for caps: %s", strcaps_v[i]);

      return NULL;
    }

    if (encoding_profile) {
      if (gst_encoding_container_profile_add_profile
          (GST_ENCODING_CONTAINER_PROFILE (encoding_profile),
              profile) == FALSE) {
        g_warning ("Can not create a preset for caps: %s", strcaps_v[i]);

        return NULL;
      }
    } else {
      encoding_profile = profile;
    }
  }
  g_strfreev (strcaps_v);

  return encoding_profile;
}

536
537
538
539
540
int
main (int argc, gchar ** argv)
{
  GError *err = NULL;
  gchar *outputuri = NULL;
541
  const gchar *format = NULL;
542
  gchar *exclude_args = NULL;
543
  static gboolean smartrender = FALSE;
544
545
  static gboolean list_transitions = FALSE;
  static gboolean list_patterns = FALSE;
546
  static gdouble thumbinterval = 0;
547
  static gboolean verbose = FALSE;
548
549
  gchar *save_path = NULL;
  gchar *load_path = NULL;
550
  GOptionEntry options[] = {
551
552
    {"thumbnail", 'm', 0.0, G_OPTION_ARG_DOUBLE, &thumbinterval,
        "Take thumbnails every n seconds (saved in current directory)", "N"},
553
554
555
556
    {"smartrender", 's', 0, G_OPTION_ARG_NONE, &smartrender,
        "Render to outputuri, and avoid decoding/reencoding", NULL},
    {"outputuri", 'o', 0, G_OPTION_ARG_STRING, &outputuri,
        "URI to encode to", "URI (<protocol>://<location>)"},
557
558
559
560
561
562
563
564
565
    {"format", 'f', 0, G_OPTION_ARG_STRING, &format,
          "Set the properties to use for the encoding profile "
          "(in case of transcoding.) For example:\n"
          "video/mpegts:video/x-raw,width=1920,height=1080->video/x-h264:audio/x-ac3\n"
          "A preset name can be used by adding +presetname, eg:\n"
          "video/webm:video/x-vp8+mypreset:audio/x-vorbis\n"
          "The presence property of the profile can be specified with |<presence>, eg:\n"
          "video/webm:video/x-vp8|<presence>:audio/x-vorbis\n",
        "properties-values"},
566
    {"repeat", 'r', 0, G_OPTION_ARG_INT, &repeat,
567
        "Number of time to repeat timeline", NULL},
568
569
570
571
    {"list-transitions", 't', 0, G_OPTION_ARG_NONE, &list_transitions,
        "List valid transition types and exit", NULL},
    {"list-patterns", 'p', 0, G_OPTION_ARG_NONE, &list_patterns,
        "List patterns and exit", NULL},
572
573
    {"save", 'z', 0, G_OPTION_ARG_STRING, &save_path,
        "Save project to file before rendering", "<path>"},
574
    {"load", 'l', 0, G_OPTION_ARG_STRING, &load_path,
575
        "Load project from file before rendering", "<path>"},
576
577
578
579
    {"verbose", 0, 0, G_OPTION_ARG_NONE, &verbose,
        "Output status information and property notifications", NULL},
    {"exclude", 'X', 0, G_OPTION_ARG_NONE, &exclude_args,
        "Do not output status information of TYPE", "TYPE1,TYPE2,..."},
580
581
    {"sample-paths", 'P', 0, G_OPTION_ARG_STRING_ARRAY, &new_paths,
        "List of pathes to look assets in if they were moved"},
582
583
584
585
    {NULL}
  };
  GOptionContext *ctx;
  GstBus *bus;
586
  GESTimeline *timeline;
587

588
589
  setlocale (LC_ALL, "");

Stefan Kost's avatar
Stefan Kost committed
590
  ctx = g_option_context_new ("- plays or renders a timeline.");
591
  g_option_context_set_summary (ctx,
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
      "ges-launch renders a timeline, which can be specified on the commandline,\n"
      "or loaded from a file using the -q option.\n\n"
      "A timeline is a list of files, patterns, and transitions to be rendered\n"
      "one after the other. Files and Patterns provide video and audio as the\n"
      "primary input, and transitions animate between the end of one file/pattern\n"
      "and the beginning of a new one. Hence, transitions can only be listed\n"
      "in between patterns or files.\n\n"
      "A file is a triplet of filename, inpoint (in seconds) and\n"
      "duration (in seconds). If the duration is 0, the full file length is used.\n\n"
      "Patterns and transitions are triplets that begin with either \"+pattern\"\n"
      "or \"+transition\", followed by a <type> and duration (in seconds, must be\n"
      "greater than 0)\n\n"
      "Durations in all cases can be fractions of a second.\n\n"
      "Example:\n"
      "ges-launch file1.avi 0 45 +transition crossfade 3.5 file2.avi 0 0");
607
608
609
610
  g_option_context_add_main_entries (ctx, options, NULL);
  g_option_context_add_group (ctx, gst_init_get_option_group ());

  if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
611
    g_printerr ("Error initializing: %s\n", err->message);
612
613
614
615
    g_option_context_free (ctx);
    exit (1);
  }

616
  /* Initialize the GStreamer Editing Services */
617
618
619
620
  if (!ges_init ()) {
    g_printerr ("Error initializing GES\n");
    exit (1);
  }
621

622
623
624
625
626
627
628
629
630
631
  if (list_transitions) {
    print_transition_list ();
    exit (0);
  }

  if (list_patterns) {
    print_pattern_list ();
    exit (0);
  }

632
  tried_uris = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
633
  if (((!load_path && (argc < 4)))) {
634
    g_printf ("%s", g_option_context_get_help (ctx, TRUE, NULL));
635
    g_option_context_free (ctx);
636
    exit (1);
637
638
639
640
641
  }

  g_option_context_free (ctx);

  /* Create the pipeline */
642
  pipeline =
643
      create_pipeline (&timeline, load_path, save_path, argc - 1, argv + 1);
644
  if (!pipeline)
645
    exit (1);
646
647

  /* Setup profile/encoding if needed */
648
649
650
  if (smartrender || outputuri) {
    GstEncodingProfile *prof = NULL;

651
652
653
    if (!format) {
      GESProject *proj =
          GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));
654
      const GList *profiles = ges_project_list_encoding_profiles (proj);
655

656
657
658
659
      prof = profiles ? profiles->data : NULL;
    }

    if (!prof) {
660
661
662
663
      if (format == NULL)
        format = "application/ogg:video/x-theora:audio/x-vorbis";

      prof = _parse_encoding_profile (format);
664
    }
665

666
667
668
    if (outputuri)
      outputuri = ensure_uri (outputuri);

669
670
    if (!prof || !ges_pipeline_set_render_settings (pipeline, outputuri, prof)
        || !ges_pipeline_set_mode (pipeline,
671
672
            smartrender ? TIMELINE_MODE_SMART_RENDER : TIMELINE_MODE_RENDER)) {
      g_free (outputuri);
673
      exit (1);
674
675
    }
    g_free (outputuri);
676

677
    gst_encoding_profile_unref (prof);
678
  } else {
679
    ges_pipeline_set_mode (pipeline, TIMELINE_MODE_PREVIEW);
680
681
  }

682
683
684
685
686
687
688
  if (verbose) {
    gchar **exclude_list =
        exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL;
    g_signal_connect (pipeline, "deep-notify",
        G_CALLBACK (gst_object_default_deep_notify), exclude_list);
  }

689
690
691
  /* Play the pipeline */
  mainloop = g_main_loop_new (NULL, FALSE);

692
  if (thumbinterval != 0.0) {
693
    g_printf ("thumbnailing every %f seconds\n", thumbinterval);
694
695
696
    g_timeout_add (1000 * thumbinterval, thumbnail_cb, pipeline);
  }

697
698
699
700
701
702
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_bus_add_signal_watch (bus);
  g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), mainloop);

  if (gst_element_set_state (GST_ELEMENT (pipeline),
          GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
703
    g_error ("Failed to start the encoding\n");
704
705
    return 1;
  }
706
  g_timeout_add (100, (GSourceFunc) _print_position, NULL);
707
708
709
710
  g_main_loop_run (mainloop);

  gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);

Edward Hervey's avatar
Edward Hervey committed
711
712
  gst_object_unref (pipeline);

713
  g_hash_table_unref (tried_uris);
714
  return (int) seenerrors;
715
}