ges-track-element.c 46.2 KB
Newer Older
Edward Hervey's avatar
Edward Hervey committed
1
/* GStreamer Editing Services
Edward Hervey's avatar
Edward Hervey committed
2
3
 * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
 *               2009 Nokia Corporation
Edward Hervey's avatar
Edward Hervey committed
4
5
6
7
8
9
10
11
12
13
14
15
16
 *
 * 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
17
18
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Edward Hervey's avatar
Edward Hervey committed
19
20
 */

21
/**
22
 * SECTION:gestrackelement
23
 * @title: GESTrackElement
Thibault Saunier's avatar
Thibault Saunier committed
24
 * @short_description: Base Class for objects contained in a GESTrack
25
 *
26
 * #GESTrackElement is the Base Class for any object that can be contained in a
27
 * #GESTrack.
28
29
 *
 * It contains the basic information as to the location of the object within
30
 * its container, like the start position, the inpoint, the duration and the
31
 * priority.
32
 */
33
34
35
36
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

37
#include "ges-internal.h"
38
#include "ges-extractable.h"
39
#include "ges-track-element.h"
40
#include "ges-clip.h"
41
#include "ges-meta-container.h"
Edward Hervey's avatar
Edward Hervey committed
42

43
struct _GESTrackElementPrivate
44
{
45
46
  GESTrackType track_type;

47
48
  GstElement *nleobject;        /* The NleObject */
  GstElement *element;          /* The element contained in the nleobject (can be NULL) */
49
50
51

  GESTrack *track;

52
  gboolean locked;              /* If TRUE, then moves in sync with its controlling
53
                                 * GESClip */
54
55
56

  GHashTable *bindings_hashtable;       /* We need this if we want to be able to serialize
                                           and deserialize keyframes */
57
58
};

59
60
61
enum
{
  PROP_0,
62
  PROP_ACTIVE,
63
  PROP_TRACK_TYPE,
64
  PROP_TRACK,
65
  PROP_LAST
66
67
};

68
69
static GParamSpec *properties[PROP_LAST];

70
71
enum
{
72
  CONTROL_BINDING_ADDED,
73
  CONTROL_BINDING_REMOVED,
74
75
76
  LAST_SIGNAL
};

77
static guint ges_track_element_signals[LAST_SIGNAL] = { 0 };
78

79
80
81
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESTrackElement, ges_track_element,
    GES_TYPE_TIMELINE_ELEMENT);

82
static GstElement *ges_track_element_create_gnl_object_func (GESTrackElement *
83
    object);
84

85
86
87
88
89
90
static gboolean _set_start (GESTimelineElement * element, GstClockTime start);
static gboolean _set_inpoint (GESTimelineElement * element,
    GstClockTime inpoint);
static gboolean _set_duration (GESTimelineElement * element,
    GstClockTime duration);
static gboolean _set_priority (GESTimelineElement * element, guint32 priority);
91
GESTrackType _get_track_types (GESTimelineElement * object);
92

93
static GParamSpec **default_list_children_properties (GESTrackElement * object,
94
95
    guint * n_properties);

96
97
98
99
static void
_update_control_bindings (GESTimelineElement * element, GstClockTime inpoint,
    GstClockTime duration);

100
101
102
103
static gboolean
_lookup_child (GESTrackElement * object,
    const gchar * prop_name, GstElement ** element, GParamSpec ** pspec)
{
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  return
      GES_TIMELINE_ELEMENT_GET_CLASS (object)->lookup_child
      (GES_TIMELINE_ELEMENT (object), prop_name, (GObject **) element, pspec);
}

static gboolean
strv_find_str (const gchar ** strv, const char *str)
{
  guint i;

  if (strv == NULL)
    return FALSE;

  for (i = 0; strv[i]; i++) {
    if (g_strcmp0 (strv[i], str) == 0)
      return TRUE;
120
121
  }

122
  return FALSE;
123
124
}

125
static void
126
ges_track_element_get_property (GObject * object, guint property_id,
127
128
    GValue * value, GParamSpec * pspec)
{
129
  GESTrackElement *track_element = GES_TRACK_ELEMENT (object);
130
131

  switch (property_id) {
132
    case PROP_ACTIVE:
133
      g_value_set_boolean (value, ges_track_element_is_active (track_element));
134
      break;
135
    case PROP_TRACK_TYPE:
136
      g_value_set_flags (value, track_element->priv->track_type);
137
      break;
138
    case PROP_TRACK:
139
      g_value_set_object (value, track_element->priv->track);
140
      break;
141
142
143
144
145
146
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
  }
}

static void
147
ges_track_element_set_property (GObject * object, guint property_id,
148
149
    const GValue * value, GParamSpec * pspec)
{
150
  GESTrackElement *track_element = GES_TRACK_ELEMENT (object);
151
152

  switch (property_id) {
153
    case PROP_ACTIVE:
154
      ges_track_element_set_active (track_element, g_value_get_boolean (value));
155
      break;
156
    case PROP_TRACK_TYPE:
157
      track_element->priv->track_type = g_value_get_flags (value);
158
      break;
159
160
161
162
163
164
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
  }
}

static void
165
ges_track_element_dispose (GObject * object)
166
{
167
168
  GESTrackElement *element = GES_TRACK_ELEMENT (object);
  GESTrackElementPrivate *priv = element->priv;
169

170
171
172
  if (priv->bindings_hashtable)
    g_hash_table_destroy (priv->bindings_hashtable);

173
  if (priv->nleobject) {
174
175
176
    GstState cstate;

    if (priv->track != NULL) {
177
      g_error ("%p Still in %p, this means that you forgot"
178
          " to remove it from the GESTrack it is contained in. You always need"
179
          " to remove a GESTrackElement from its track before dropping the last"
180
181
          " reference\n"
          "This problem may also be caused by a refcounting bug in"
182
          " the application or GES itself.", object, priv->track);
183
      gst_element_get_state (priv->nleobject, &cstate, NULL, 0);
184
      if (cstate != GST_STATE_NULL)
185
        gst_element_set_state (priv->nleobject, GST_STATE_NULL);
186
187
    }

188
189
190
191
    g_object_set_qdata (G_OBJECT (priv->nleobject),
        NLE_OBJECT_TRACK_ELEMENT_QUARK, NULL);
    gst_object_unref (priv->nleobject);
    priv->nleobject = NULL;
192
193
  }

194
  G_OBJECT_CLASS (ges_track_element_parent_class)->dispose (object);
195
196
197
}

static void
198
ges_track_element_constructed (GObject * gobject)
199
{
200
201
  GESTrackElementClass *class;
  GstElement *nleobject;
Sjors Gielen's avatar
Sjors Gielen committed
202
  gdouble media_duration_factor;
203
  gchar *tmp;
204
205
206
207
208
209
210
211
212
213
  GESTrackElement *object = GES_TRACK_ELEMENT (gobject);

  GST_DEBUG_OBJECT (object, "Creating NleObject");

  class = GES_TRACK_ELEMENT_GET_CLASS (object);
  g_assert (class->create_gnl_object);

  nleobject = class->create_gnl_object (object);
  if (G_UNLIKELY (nleobject == NULL)) {
    GST_ERROR_OBJECT (object, "Could not create NleObject");
214

215
216
217
    return;
  }

218
219
220
221
222
  tmp = g_strdup_printf ("%s:%s", G_OBJECT_TYPE_NAME (object),
      GST_OBJECT_NAME (nleobject));
  gst_object_set_name (GST_OBJECT (nleobject), tmp);
  g_free (tmp);

223
224
225
226
227
228
229
230
231
232
233
234
235
236
  GST_DEBUG_OBJECT (object, "Got a valid NleObject, now filling it in");

  object->priv->nleobject = gst_object_ref (nleobject);
  g_object_set_qdata (G_OBJECT (nleobject), NLE_OBJECT_TRACK_ELEMENT_QUARK,
      object);

  /* Set some properties on the NleObject */
  g_object_set (object->priv->nleobject,
      "start", GES_TIMELINE_ELEMENT_START (object),
      "inpoint", GES_TIMELINE_ELEMENT_INPOINT (object),
      "duration", GES_TIMELINE_ELEMENT_DURATION (object),
      "priority", GES_TIMELINE_ELEMENT_PRIORITY (object),
      "active", object->active, NULL);

Sjors Gielen's avatar
Sjors Gielen committed
237
238
239
240
241
242
  media_duration_factor =
      ges_timeline_element_get_media_duration_factor (GES_TIMELINE_ELEMENT
      (object));
  g_object_set (object->priv->nleobject,
      "media-duration-factor", media_duration_factor, NULL);

243
  G_OBJECT_CLASS (ges_track_element_parent_class)->constructed (gobject);
244
245
246
}

static void
247
ges_track_element_class_init (GESTrackElementClass * klass)
248
249
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
250
  GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
251

252
253
254
  object_class->get_property = ges_track_element_get_property;
  object_class->set_property = ges_track_element_set_property;
  object_class->dispose = ges_track_element_dispose;
255
  object_class->constructed = ges_track_element_constructed;
256

257

258
  /**
259
   * GESTrackElement:active:
260
   *
Brandon Lewis's avatar
Brandon Lewis committed
261
   * Whether the object should be taken into account in the #GESTrack output.
262
263
   * If #FALSE, then its contents will not be used in the resulting track.
   */
264
265
266
  properties[PROP_ACTIVE] =
      g_param_spec_boolean ("active", "Active", "Use object in output", TRUE,
      G_PARAM_READWRITE);
267
  g_object_class_install_property (object_class, PROP_ACTIVE,
268
      properties[PROP_ACTIVE]);
269

270
271
272
273
274
275
  properties[PROP_TRACK_TYPE] = g_param_spec_flags ("track-type", "Track Type",
      "The track type of the object", GES_TYPE_TRACK_TYPE,
      GES_TRACK_TYPE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
  g_object_class_install_property (object_class, PROP_TRACK_TYPE,
      properties[PROP_TRACK_TYPE]);

276
277
278
279
280
  properties[PROP_TRACK] = g_param_spec_object ("track", "Track",
      "The track the object is in", GES_TYPE_TRACK, G_PARAM_READABLE);
  g_object_class_install_property (object_class, PROP_TRACK,
      properties[PROP_TRACK]);

281
282
283
284
285
  /**
   * GESTrackElement::control-binding-added:
   * @track_element: a #GESTrackElement
   * @control_binding: the #GstControlBinding that has been added
   *
286
   * The control-binding-added signal is emitted each time a control binding
287
288
289
290
291
292
293
   * is added for a child property of @track_element
   */
  ges_track_element_signals[CONTROL_BINDING_ADDED] =
      g_signal_new ("control-binding-added", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
      G_TYPE_NONE, 1, GST_TYPE_CONTROL_BINDING);

294
  /**
295
   * GESTrackElement::control-binding-removed:
296
   * @track_element: a #GESTrackElement
297
   * @control_binding: the #GstControlBinding that has been removed
298
   *
299
300
   * The control-binding-removed signal is emitted each time a control binding
   * is removed for a child property of @track_element
301
302
   */
  ges_track_element_signals[CONTROL_BINDING_REMOVED] =
303
      g_signal_new ("control-binding-removed", G_TYPE_FROM_CLASS (klass),
304
305
306
      G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
      G_TYPE_NONE, 1, GST_TYPE_CONTROL_BINDING);

307
308
309
310
  element_class->set_start = _set_start;
  element_class->set_duration = _set_duration;
  element_class->set_inpoint = _set_inpoint;
  element_class->set_priority = _set_priority;
311
  element_class->get_track_types = _get_track_types;
312
  element_class->deep_copy = ges_track_element_copy_properties;
313

314
  klass->create_gnl_object = ges_track_element_create_gnl_object_func;
315
  klass->list_children_properties = default_list_children_properties;
316
  klass->lookup_child = _lookup_child;
317
318
319
}

static void
320
ges_track_element_init (GESTrackElement * self)
321
{
322
323
  GESTrackElementPrivate *priv = self->priv =
      ges_track_element_get_instance_private (self);
324

325
  /* Sane default values */
326
327
328
329
330
331
  GES_TIMELINE_ELEMENT_START (self) = 0;
  GES_TIMELINE_ELEMENT_INPOINT (self) = 0;
  GES_TIMELINE_ELEMENT_DURATION (self) = GST_SECOND;
  GES_TIMELINE_ELEMENT_PRIORITY (self) = 0;
  self->active = TRUE;

332
333
  priv->bindings_hashtable = g_hash_table_new_full (g_str_hash, g_str_equal,
      g_free, NULL);
334
335
}

336
337
static gfloat
interpolate_values_for_position (GstTimedValue * first_value,
338
    GstTimedValue * second_value, guint64 position, gboolean absolute)
339
340
341
342
343
{
  gfloat diff;
  GstClockTime interval;
  gfloat value_at_pos;

344
345
346
347
348
349
350
351
  g_assert (second_value || first_value);

  if (first_value == NULL)
    return second_value->value;

  if (second_value == NULL)
    return first_value->value;

352
353
354
355
356
357
358
359
360
361
362
363
  diff = second_value->value - first_value->value;
  interval = second_value->timestamp - first_value->timestamp;

  if (position > first_value->timestamp)
    value_at_pos =
        first_value->value + ((float) (position -
            first_value->timestamp) / (float) interval) * diff;
  else
    value_at_pos =
        first_value->value - ((float) (first_value->timestamp -
            position) / (float) interval) * diff;

364
365
  if (!absolute)
    value_at_pos = CLAMP (value_at_pos, 0.0, 1.0);
366

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
  return value_at_pos;
}

static void
_update_control_bindings (GESTimelineElement * element, GstClockTime inpoint,
    GstClockTime duration)
{
  GParamSpec **specs;
  guint n, n_specs;
  GstControlBinding *binding;
  GstTimedValueControlSource *source;
  GESTrackElement *self = GES_TRACK_ELEMENT (element);

  specs = ges_track_element_list_children_properties (self, &n_specs);

  for (n = 0; n < n_specs; ++n) {
    GList *values, *tmp;
384
    gboolean absolute;
385
386
387
388
389
390
391
392
393
394
    GstTimedValue *last, *first, *prev = NULL, *next = NULL;
    gfloat value_at_pos;

    binding = ges_track_element_get_control_binding (self, specs[n]->name);

    if (!binding)
      continue;

    g_object_get (binding, "control_source", &source, NULL);

395
    g_object_get (binding, "absolute", &absolute, NULL);
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
    if (duration == 0) {
      gst_timed_value_control_source_unset_all (GST_TIMED_VALUE_CONTROL_SOURCE
          (source));
      continue;
    }

    values =
        gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
        (source));

    if (g_list_length (values) == 0)
      continue;

    first = values->data;

    for (tmp = values->next; tmp; tmp = tmp->next) {
      next = tmp->data;

      if (next->timestamp > inpoint)
        break;
    }

418
419
    value_at_pos =
        interpolate_values_for_position (first, next, inpoint, absolute);
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
    gst_timed_value_control_source_unset (source, first->timestamp);
    gst_timed_value_control_source_set (source, inpoint, value_at_pos);

    values =
        gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
        (source));

    if (duration != GST_CLOCK_TIME_NONE) {
      last = g_list_last (values)->data;

      for (tmp = g_list_last (values)->prev; tmp; tmp = tmp->prev) {
        prev = tmp->data;

        if (prev->timestamp < duration + inpoint)
          break;
      }

      value_at_pos =
438
439
          interpolate_values_for_position (prev, last, duration + inpoint,
          absolute);
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461

      gst_timed_value_control_source_unset (source, last->timestamp);
      gst_timed_value_control_source_set (source, duration + inpoint,
          value_at_pos);
      values =
          gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
          (source));
    }

    for (tmp = values; tmp; tmp = tmp->next) {
      GstTimedValue *value = tmp->data;
      if (value->timestamp < inpoint)
        gst_timed_value_control_source_unset (source, value->timestamp);
      else if (duration != GST_CLOCK_TIME_NONE
          && value->timestamp > duration + inpoint)
        gst_timed_value_control_source_unset (source, value->timestamp);
    }
  }

  g_free (specs);
}

462
463
static gboolean
_set_start (GESTimelineElement * element, GstClockTime start)
464
{
465
  GESTrackElement *object = GES_TRACK_ELEMENT (element);
466

467
468
469
470
  g_return_val_if_fail (object->priv->nleobject, FALSE);

  if (G_UNLIKELY (start == _START (object)))
    return FALSE;
471

472
  g_object_set (object->priv->nleobject, "start", start, NULL);
473

474
  return TRUE;
475
476
}

477
478
static gboolean
_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
479
{
480
  GESTrackElement *object = GES_TRACK_ELEMENT (element);
481

482
  g_return_val_if_fail (object->priv->nleobject, FALSE);
483

484
  if (G_UNLIKELY (inpoint == _INPOINT (object)))
485

486
    return FALSE;
487

488
  g_object_set (object->priv->nleobject, "inpoint", inpoint, NULL);
489
490
  _update_control_bindings (element, inpoint, GST_CLOCK_TIME_NONE);

491
492
493
  return TRUE;
}

494
495
static gboolean
_set_duration (GESTimelineElement * element, GstClockTime duration)
496
{
497
498
  GESTrackElement *object = GES_TRACK_ELEMENT (element);
  GESTrackElementPrivate *priv = object->priv;
499

500
501
  g_return_val_if_fail (object->priv->nleobject, FALSE);

502
503
504
  if (GST_CLOCK_TIME_IS_VALID (_MAXDURATION (element)) &&
      duration > _INPOINT (object) + _MAXDURATION (element))
    duration = _MAXDURATION (element) - _INPOINT (object);
505

506
507
  if (G_UNLIKELY (duration == _DURATION (object)))
    return FALSE;
508

509
  g_object_set (priv->nleobject, "duration", duration, NULL);
510

511
512
513
  _update_control_bindings (element, ges_timeline_element_get_inpoint (element),
      duration);

514
515
516
  return TRUE;
}

517
518
static gboolean
_set_priority (GESTimelineElement * element, guint32 priority)
519
{
520
  GESTrackElement *object = GES_TRACK_ELEMENT (element);
521

522
523
  g_return_val_if_fail (object->priv->nleobject, FALSE);

524
525
526
527
  if (priority < MIN_NLE_PRIO) {
    GST_INFO_OBJECT (element, "Priority (%d) < MIN_NLE_PRIO, setting it to %d",
        priority, MIN_NLE_PRIO);
    priority = MIN_NLE_PRIO;
528
529
  }

530
  GST_DEBUG_OBJECT (object, "priority:%" G_GUINT32_FORMAT, priority);
531

532
533
  if (G_UNLIKELY (priority == _PRIORITY (object)))
    return FALSE;
534

535
  g_object_set (object->priv->nleobject, "priority", priority, NULL);
536

537
  return TRUE;
538
539
}

540
541
542
543
544
545
GESTrackType
_get_track_types (GESTimelineElement * object)
{
  return ges_track_element_get_track_type (GES_TRACK_ELEMENT (object));
}

546
/**
547
548
 * ges_track_element_set_active:
 * @object: a #GESTrackElement
549
550
551
552
553
554
555
 * @active: visibility
 *
 * Sets the usage of the @object. If @active is %TRUE, the object will be used for
 * playback and rendering, else it will be ignored.
 *
 * Returns: %TRUE if the property was toggled, else %FALSE
 */
556
gboolean
557
ges_track_element_set_active (GESTrackElement * object, gboolean active)
558
{
559
  g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
560
  g_return_val_if_fail (object->priv->nleobject, FALSE);
561

562
  GST_DEBUG_OBJECT (object, "object:%p, active:%d", object, active);
563

564
565
  if (G_UNLIKELY (active == object->active))
    return FALSE;
566

567
  g_object_set (object->priv->nleobject, "active", active, NULL);
568

569
570
571
572
573
  if (active != object->active) {
    object->active = active;
    if (GES_TRACK_ELEMENT_GET_CLASS (object)->active_changed)
      GES_TRACK_ELEMENT_GET_CLASS (object)->active_changed (object, active);
  }
574

575
576
577
  return TRUE;
}

578
void
579
ges_track_element_set_track_type (GESTrackElement * object, GESTrackType type)
580
{
581
  g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
582
583
584
585
586
587
588
589

  if (object->priv->track_type != type) {
    object->priv->track_type = type;
    g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_TRACK_TYPE]);
  }
}

GESTrackType
590
ges_track_element_get_track_type (GESTrackElement * object)
591
{
592
  g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), GES_TRACK_TYPE_UNKNOWN);
593
594
595
596

  return object->priv->track_type;
}

597
/* default 'create_gnl_object' virtual method implementation */
598
static GstElement *
599
ges_track_element_create_gnl_object_func (GESTrackElement * self)
600
{
601
  GESTrackElementClass *klass = NULL;
602
  GstElement *child = NULL;
603
  GstElement *nleobject;
604

605
  klass = GES_TRACK_ELEMENT_GET_CLASS (self);
606

607
608
  if (G_UNLIKELY (self->priv->nleobject != NULL))
    goto already_have_nleobject;
609

610
611
  if (G_UNLIKELY (klass->nleobject_factorytype == NULL))
    goto no_nlefactory;
612

613
614
  GST_DEBUG ("Creating a supporting nleobject of type '%s'",
      klass->nleobject_factorytype);
615

616
  nleobject = gst_element_factory_make (klass->nleobject_factorytype, NULL);
617

618
619
  if (G_UNLIKELY (nleobject == NULL))
    goto no_nleobject;
620
621
622
623
624
625
626
627

  if (klass->create_element) {
    GST_DEBUG ("Calling subclass 'create_element' vmethod");
    child = klass->create_element (self);

    if (G_UNLIKELY (!child))
      goto child_failure;

628
    if (!gst_bin_add (GST_BIN (nleobject), child))
629
630
      goto add_failure;

631
    GST_DEBUG ("Succesfully got the element to put in the nleobject");
632
    self->priv->element = child;
633
634
635
  }

  GST_DEBUG ("done");
636
  return nleobject;
637
638
639
640


  /* ERROR CASES */

641
already_have_nleobject:
642
  {
643
644
    GST_ERROR ("Already controlling a NleObject %s",
        GST_ELEMENT_NAME (self->priv->nleobject));
645
    return NULL;
646
647
  }

648
no_nlefactory:
649
  {
650
    GST_ERROR ("No GESTrackElement::nleobject_factorytype implementation!");
651
    return NULL;
652
653
  }

654
no_nleobject:
655
  {
656
657
    GST_ERROR ("Error creating a nleobject of type '%s'",
        klass->nleobject_factorytype);
658
    return NULL;
659
660
661
662
663
  }

child_failure:
  {
    GST_ERROR ("create_element returned NULL");
664
    gst_object_unref (nleobject);
665
    return NULL;
666
667
668
669
  }

add_failure:
  {
670
    GST_ERROR ("Error adding the contents to the nleobject");
671
    gst_object_unref (child);
672
    gst_object_unref (nleobject);
673
    return NULL;
674
  }
675
676
}

677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
static void
ges_track_element_add_child_props (GESTrackElement * self,
    GstElement * child, const gchar ** wanted_categories,
    const gchar ** blacklist, const gchar ** whitelist)
{
  GstElementFactory *factory;
  const gchar *klass;
  GParamSpec **parray;
  GObjectClass *gobject_klass;
  gchar **categories;
  guint i;

  factory = gst_element_get_factory (child);
  klass = gst_element_factory_get_metadata (factory,
      GST_ELEMENT_METADATA_KLASS);

  if (strv_find_str (blacklist, GST_OBJECT_NAME (factory))) {
    GST_DEBUG_OBJECT (self, "%s blacklisted", GST_OBJECT_NAME (factory));
    return;
  }

  GST_DEBUG_OBJECT (self, "Looking at element '%s' of klass '%s'",
      GST_ELEMENT_NAME (child), klass);

  categories = g_strsplit (klass, "/", 0);

  for (i = 0; categories[i]; i++) {
    if ((!wanted_categories ||
            strv_find_str (wanted_categories, categories[i]))) {
      guint i, nb_specs;

      gobject_klass = G_OBJECT_GET_CLASS (child);
      parray = g_object_class_list_properties (gobject_klass, &nb_specs);
      for (i = 0; i < nb_specs; i++) {
711
712
        if ((!whitelist && (parray[i]->flags & G_PARAM_WRITABLE))
            || (strv_find_str (whitelist, parray[i]->name))) {
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
          ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT
              (self), parray[i], G_OBJECT (child));
        }
      }
      g_free (parray);

      GST_DEBUG
          ("%d configurable properties of '%s' added to property hashtable",
          nb_specs, GST_ELEMENT_NAME (child));
      break;
    }
  }

  g_strfreev (categories);
}

729
730
731
732
733
734
735
736
737
/**
 * ges_track_element_add_children_props:
 * @self: The #GESTrackElement to set chidlren props on
 * @element: The GstElement to retrieve properties from
 * @wanted_categories: (array zero-terminated=1) (transfer none) (allow-none):
 * An array of categories of GstElement to
 * take into account (as defined in the factory meta "klass" field)
 * @blacklist: (array zero-terminated=1) (transfer none) (allow-none): A
 * blacklist of elements factory names to not take into account
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
738
 * @whitelist: (array zero-terminated=1) (transfer none) (allow-none): A list
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
 * of propery names to add as children properties
 *
 * Looks for the properties defines with the various parametters and add
 * them to the hashtable of children properties.
 *
 * To be used by subclasses only
 */
void
ges_track_element_add_children_props (GESTrackElement * self,
    GstElement * element, const gchar ** wanted_categories,
    const gchar ** blacklist, const gchar ** whitelist)
{
  GValue item = { 0, };
  GstIterator *it;
  gboolean done = FALSE;

  if (!GST_IS_BIN (element)) {
756
757
    ges_track_element_add_child_props (self, element, wanted_categories,
        blacklist, whitelist);
758
759
760
761
762
763
764
765
766
767
768
    return;
  }

  /*  We go over child elements recursivly, and add writable properties to the
   *  hashtable */
  it = gst_bin_iterate_recurse (GST_BIN (element));
  while (!done) {
    switch (gst_iterator_next (it, &item)) {
      case GST_ITERATOR_OK:
      {
        GstElement *child = g_value_get_object (&item);
769
770
        ges_track_element_add_child_props (self, child, wanted_categories,
            blacklist, whitelist);
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
        g_value_reset (&item);
        break;
      }
      case GST_ITERATOR_RESYNC:
        /* FIXME, properly restart the process */
        GST_DEBUG ("iterator resync");
        gst_iterator_resync (it);
        break;

      case GST_ITERATOR_DONE:
        GST_DEBUG ("iterator done");
        done = TRUE;
        break;

      default:
        break;
    }
    g_value_unset (&item);
  }
  gst_iterator_free (it);
}

793
/* INTERNAL USAGE */
794
gboolean
795
ges_track_element_set_track (GESTrackElement * object, GESTrack * track)
796
{
797
  gboolean ret = TRUE;
798
799
800
801

  g_return_val_if_fail (object->priv->nleobject, FALSE);

  GST_DEBUG_OBJECT (object, "new track: %" GST_PTR_FORMAT, track);
802

803
  object->priv->track = track;
804

805
  if (object->priv->track) {
806
807
    ges_track_element_set_track_type (object, track->type);

808
809
    g_object_set (object->priv->nleobject,
        "caps", ges_track_get_caps (object->priv->track), NULL);
810
  }
811

812
813
  g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_TRACK]);
  return ret;
814
815
}

816
817
818
819
820
821
822
/**
 * ges_track_element_get_all_control_bindings
 * @trackelement: The #TrackElement from which to get all set bindings
 *
 * Returns: (element-type gchar* GstControlBinding)(transfer none): A
 * #GHashTable containing all property_name: GstControlBinding
 */
823
GHashTable *
824
ges_track_element_get_all_control_bindings (GESTrackElement * trackelement)
825
826
827
828
829
830
{
  GESTrackElementPrivate *priv = GES_TRACK_ELEMENT (trackelement)->priv;

  return priv->bindings_hashtable;
}

831
832
833
guint32
_ges_track_element_get_layer_priority (GESTrackElement * element)
{
834
  if (_PRIORITY (element) < LAYER_HEIGHT + MIN_NLE_PRIO)
835
836
    return 0;

837
  return (_PRIORITY (element) - MIN_NLE_PRIO) / LAYER_HEIGHT;
838
839
}

840
/**
841
842
 * ges_track_element_get_track:
 * @object: a #GESTrackElement
843
 *
844
 * Get the #GESTrack to which this object belongs.
845
 *
846
847
 * Returns: (transfer none) (nullable): The #GESTrack to which this object
 * belongs. Can be %NULL if it is not in any track
848
849
 */
GESTrack *
850
ges_track_element_get_track (GESTrackElement * object)
851
{
852
  g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
853
854
855
856

  return object->priv->track;
}

857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
/**
 * ges_track_element_get_gnlobject:
 * @object: a #GESTrackElement
 *
 * Get the NleObject object this object is controlling.
 *
 * Returns: (transfer none): the NleObject object this object is controlling.
 *
 * Deprecated: use #ges_track_element_get_nleobject instead.
 */
GstElement *
ges_track_element_get_gnlobject (GESTrackElement * object)
{
  g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);

  return object->priv->nleobject;
}

875
/**
876
 * ges_track_element_get_nleobject:
877
 * @object: a #GESTrackElement
878
 *
879
880
 * Get the GNonLin object this object is controlling.
 *
881
 * Returns: (transfer none): the GNonLin object this object is controlling.
882
883
 *
 * Since: 1.6
884
885
 */
GstElement *
886
ges_track_element_get_nleobject (GESTrackElement * object)
887
{
888
  g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
889

890
  return object->priv->nleobject;
891
892
893
}

/**
894
895
 * ges_track_element_get_element:
 * @object: a #GESTrackElement
896
 *
897
 * Get the #GstElement this track element is controlling within GNonLin.
898
 *
899
 * Returns: (transfer none): the #GstElement this track element is controlling
900
901
902
 * within GNonLin.
 */
GstElement *
903
ges_track_element_get_element (GESTrackElement * object)
904
{
905
  g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
906

907
908
  return object->priv->element;
}
909

910
/**
911
912
 * ges_track_element_is_active:
 * @object: a #GESTrackElement
913
914
915
916
917
918
919
 *
 * Lets you know if @object will be used for playback and rendering,
 * or not.
 *
 * Returns: %TRUE if @object is active, %FALSE otherwize
 */
gboolean
920
ges_track_element_is_active (GESTrackElement * object)
921
{
922
  g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
923
  g_return_val_if_fail (object->priv->nleobject, FALSE);
924

925
  return object->active;
926
}
927

Edward Hervey's avatar
Edward Hervey committed
928
/**
929
 * ges_track_element_lookup_child:
930
931
 * @object: object to lookup the property in
 * @prop_name: name of the property to look up. You can specify the name of the
Edward Hervey's avatar
Edward Hervey committed
932
 *     class as such: "ClassName::property-name", to guarantee that you get the
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
 *     proper GParamSpec in case various GstElement-s contain the same property
 *     name. If you don't do so, you will get the first element found, having
 *     this property and the and the corresponding GParamSpec.
 * @element: (out) (allow-none) (transfer full): pointer to a #GstElement that
 *     takes the real object to set property on
 * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
 *     describing the property
 *
 * Looks up which @element and @pspec would be effected by the given @name. If various
 * contained elements have this property name you will get the first one, unless you
 * specify the class name in @name.
 *
 * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
 * case the values for @pspec and @element are not modified. Unref @element after
 * usage.
948
949
 *
 * Deprecated: Use #ges_timeline_element_lookup_child
950
951
 */
gboolean
952
953
ges_track_element_lookup_child (GESTrackElement * object,
    const gchar * prop_name, GstElement ** element, GParamSpec ** pspec)
954
{
955
956
  return ges_timeline_element_lookup_child (GES_TIMELINE_ELEMENT (object),
      prop_name, ((GObject **) element), pspec);
957
958
}

959
/**
960
 * ges_track_element_set_child_property_by_pspec: (skip):
961
 * @object: a #GESTrackElement
962
 * @pspec: The #GParamSpec that specifies the property you want to set
963
964
 * @value: the value
 *
965
 * Sets a property of a child of @object.
966
967
 *
 * Deprecated: Use #ges_timeline_element_set_child_property_by_spec
968
969
 */
void
970
ges_track_element_set_child_property_by_pspec (GESTrackElement * object,
971
    GParamSpec * pspec, GValue * value)
972
{
973
  g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
974

975
976
  ges_timeline_element_set_child_property_by_pspec (GES_TIMELINE_ELEMENT
      (object), pspec, value);
977
978

  return;
979
}
980
981

/**
982
 * ges_track_element_set_child_property_valist: (skip):