nlecomposition.c 91.2 KB
Newer Older
1
2
3
/* GStreamer
 * Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
 *               2004-2008 Edward Hervey <bilboed@bilboed.com>
4
5
 *               2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
 *               2014 Thibault Saunier <tsaunier@gnome.org>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 *
 * 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
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

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

27
#include "nle.h"
28
29

/**
30
 * SECTION:element-nlecomposition
31
 *
32
 * A NleComposition contains NleObjects such as NleSources and NleOperations,
33
34
35
 * and connects them dynamically to create a composition timeline.
 */

36
static GstStaticPadTemplate nle_composition_src_template =
37
38
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
39
    GST_PAD_ALWAYS,
40
41
    GST_STATIC_CAPS_ANY);

42
43
GST_DEBUG_CATEGORY_STATIC (nlecomposition_debug);
#define GST_CAT_DEFAULT nlecomposition_debug
44
45

#define _do_init              \
46
47
48
  GST_DEBUG_CATEGORY_INIT (nlecomposition_debug,"nlecomposition", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Composition");
#define nle_composition_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (NleComposition, nle_composition, NLE_TYPE_OBJECT,
49
50
51
52
53
54
55
56
57
    _do_init);

enum
{
  PROP_0,
  PROP_DEACTIVATED_ELEMENTS_STATE,
  PROP_LAST,
};

58
/* Properties from NleObject */
59
60
enum
{
61
62
63
64
  NLEOBJECT_PROP_START,
  NLEOBJECT_PROP_STOP,
  NLEOBJECT_PROP_DURATION,
  NLEOBJECT_PROP_LAST
65
66
67
68
69
};

enum
{
  COMMIT_SIGNAL,
70
  COMMITED_SIGNAL,
71
  QUERY_POSITION_SIGNAL,
72
73
74
  LAST_SIGNAL
};

75
76
77
78
79
typedef enum
{
  COMP_UPDATE_STACK_INITIALIZE,
  COMP_UPDATE_STACK_ON_COMMIT,
  COMP_UPDATE_STACK_ON_EOS,
80
81
  COMP_UPDATE_STACK_ON_SEEK,
  COMP_UPDATE_STACK_NONE
82
} NleUpdateStackReason;
83
84
85
86
87

static const char *UPDATE_PIPELINE_REASONS[] = {
  "Initialize", "Commit", "EOS", "Seek"
};

88
89
typedef struct
{
90
  NleComposition *comp;
91
92
93
  GstEvent *event;
} SeekData;

94
95
typedef struct
{
96
97
  NleComposition *comp;
  NleObject *object;
98
99
} ChildIOData;

100
101
typedef struct
{
102
  NleComposition *comp;
103
104
  gint32 seqnum;

105
  NleUpdateStackReason reason;
106
107
} UpdateCompositionData;

108
struct _NleCompositionPrivate
109
110
111
112
{
  gboolean dispose_has_run;

  /*
113
     Sorted List of NleObjects , ThreadSafe
114
115
     objects_start : sorted by start-time then priority
     objects_stop : sorted by stop-time then priority
116
     objects_hash : contains all controlled objects
117
118
119

     Those list should be manipulated exclusively in the main context
     or while the task is totally stopped.
120
121
122
123
124
   */
  GList *objects_start;
  GList *objects_stop;
  GHashTable *objects_hash;

125
  /* List of NleObject to be inserted or removed from the composition on the
126
127
128
   * next commit */
  GHashTable *pending_io;

129
130
  gulong ghosteventprobe;

131
  /* current stack, list of NleObject* */
132
133
  GNode *current;

134
  /* List of NleObject whose start/duration will be the same as the composition */
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  GList *expandables;

  /*
     current segment seek start/stop time.
     Reconstruct pipeline ONLY if seeking outside of those values
     FIXME : segment_start isn't always the earliest time before which the
     timeline doesn't need to be modified
   */
  GstClockTime segment_start;
  GstClockTime segment_stop;

  /* Seek segment handler */
  GstSegment *segment;
  GstSegment *outside_segment;

  /* Next running base_time to set on outgoing segment */
  guint64 next_base_time;

  /*
     OUR sync_handler on the child_bus
155
     We are called before nle_object_sync_handler
156
   */
157
  GstPadEventFunction nle_event_pad_func;
158
159
  gboolean send_stream_start;

160
161
162
163
  /* Protect the actions list */
  GMutex actions_lock;
  GCond actions_cond;
  GList *actions;
164

165
  gboolean running;
166
  gboolean initialized;
167

168
  GstElement *current_bin;
169
170

  gboolean seeking_itself;
171
  gint real_eos_seqnum;
172
  gint next_eos_seqnum;
173
  guint32 flush_seqnum;
174

175
  /* 0 means that we already received the right caps or segment */
176
  gint seqnum_to_restart_task;
177
  gboolean waiting_for_buffer;
178
179

  gboolean tearing_down_stack;
180
181

  NleUpdateStackReason updating_reason;
182
183
};

184
185
186
187
188
189
190
191
typedef struct _Action
{
  GCClosure closure;
  gint priority;
} Action;

#define ACTION_CALLBACK(__action) (((GCClosure*) (__action))->callback)

192
193
static guint _signals[LAST_SIGNAL] = { 0 };

194
static GParamSpec *nleobject_properties[NLEOBJECT_PROP_LAST];
195
196

#define OBJECT_IN_ACTIVE_SEGMENT(comp,element)      \
197
198
  ((NLE_OBJECT_START(element) < comp->priv->segment_stop) &&  \
   (NLE_OBJECT_STOP(element) >= comp->priv->segment_start))
199

200
201
202
static void nle_composition_dispose (GObject * object);
static void nle_composition_finalize (GObject * object);
static void nle_composition_reset (NleComposition * comp);
203

204
static gboolean nle_composition_add_object (GstBin * bin, GstElement * element);
205
206

static gboolean
207
nle_composition_remove_object (GstBin * bin, GstElement * element);
208
209

static GstStateChangeReturn
210
nle_composition_change_state (GstElement * element, GstStateChange transition);
211

212
static inline void nle_composition_reset_target_pad (NleComposition * comp);
213
214

static gboolean
215
216
217
218
219
seek_handling (NleComposition * comp, gint32 seqnum,
    NleUpdateStackReason update_stack_reason);
static gint objects_start_compare (NleObject * a, NleObject * b);
static gint objects_stop_compare (NleObject * a, NleObject * b);
static GstClockTime get_current_position (NleComposition * comp);
220

221
static gboolean update_pipeline (NleComposition * comp,
222
    GstClockTime currenttime, gint32 seqnum,
223
224
    NleUpdateStackReason update_stack_reason);
static gboolean nle_composition_commit_func (NleObject * object,
225
    gboolean recurse);
226
static void update_start_stop_duration (NleComposition * comp);
227

228
static gboolean
229
nle_composition_event_handler (GstPad * ghostpad, GstObject * parent,
230
    GstEvent * event);
231
static void _relink_single_node (NleComposition * comp, GNode * node,
232
    GstEvent * toplevel_seek);
233
234
235
236
static void _update_pipeline_func (NleComposition * comp,
    UpdateCompositionData * ucompo);
static void _commit_func (NleComposition * comp,
    UpdateCompositionData * ucompo);
237
static GstEvent *get_new_seek_event (NleComposition * comp, gboolean initial,
238
    gboolean updatestoponly);
239
240
241
242
static gboolean _nle_composition_add_object (NleComposition * comp,
    NleObject * object);
static gboolean _nle_composition_remove_object (NleComposition * comp,
    NleObject * object);
243
static void _deactivate_stack (NleComposition * comp,
244
    gboolean flush_downstream);
245
static gboolean _set_real_eos_seqnum_from_seek (NleComposition * comp,
246
    GstEvent * event);
247
static void _emit_commited_signal_func (NleComposition * comp, gpointer udata);
248
static void _restart_task (NleComposition * comp);
249
250
251
static void
_add_action (NleComposition * comp, GCallback func, gpointer data,
    gint priority);
252
253
static gboolean
_is_ready_to_restart_task (NleComposition * comp, GstEvent * event);
254

255
256
257

/* COMP_REAL_START: actual position to start current playback at. */
#define COMP_REAL_START(comp)                                                  \
258
  (MAX (comp->priv->segment->start, NLE_OBJECT_START (comp)))
259
260
261

#define COMP_REAL_STOP(comp)                                                   \
  (GST_CLOCK_TIME_IS_VALID (comp->priv->segment->stop) ?                       \
262
263
   (MIN (comp->priv->segment->stop, NLE_OBJECT_STOP (comp))) :                 \
   NLE_OBJECT_STOP (comp))
264

265
266
#define ACTIONS_LOCK(comp) G_STMT_START {                       \
  GST_LOG_OBJECT (comp, "Getting ACTIONS_LOCK in thread %p",    \
267
        g_thread_self());                                            \
268
269
  g_mutex_lock(&((NleComposition*)comp)->priv->actions_lock);    \
  GST_LOG_OBJECT (comp, "Got ACTIONS_LOCK in thread %p",        \
270
271
        g_thread_self());                                            \
} G_STMT_END
272

273
274
275
#define ACTIONS_UNLOCK(comp) G_STMT_START {                     \
  g_mutex_unlock(&((NleComposition*)comp)->priv->actions_lock);  \
  GST_LOG_OBJECT (comp, "Unlocked ACTIONS_LOCK in thread %p",   \
276
277
        g_thread_self());                                            \
} G_STMT_END
278

279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#define WAIT_FOR_AN_ACTION(comp) G_STMT_START {                     \
  GST_LOG_OBJECT (comp, "Waiting for an action in thread %p",       \
        g_thread_self());                                           \
  g_cond_wait(&((NleComposition*)comp)->priv->actions_cond,         \
      &((NleComposition*)comp)->priv->actions_lock);                  \
  GST_LOG_OBJECT (comp, "Done WAITING for an action in thread %p",       \
        g_thread_self());                                           \
} G_STMT_END

#define SIGNAL_NEW_ACTION(comp) G_STMT_START {                     \
  GST_LOG_OBJECT (comp, "Signalling new action from thread %p",       \
        g_thread_self());                                           \
  g_cond_signal(&((NleComposition*)comp)->priv->actions_cond);     \
} G_STMT_END

294
#define GET_TASK_LOCK(comp)    (&(NLE_COMPOSITION(comp)->task_rec_lock))
295

296
static inline gboolean
297
_have_to_flush_downstream (NleUpdateStackReason update_reason)
298
299
{
  if (update_reason == COMP_UPDATE_STACK_ON_COMMIT ||
300
301
      update_reason == COMP_UPDATE_STACK_ON_SEEK ||
      update_reason == COMP_UPDATE_STACK_INITIALIZE)
302
303
304
305
306
    return TRUE;

  return FALSE;
}

307
static void
308
_assert_proper_thread (NleComposition * comp)
309
310
311
312
313
314
315
316
317
{
  if (comp->task && gst_task_get_state (comp->task) != GST_TASK_STOPPED &&
      g_thread_self () != comp->task->thread) {
    g_warning ("Trying to touch children in a thread different from"
        " its dedicated thread!");
  }
}

static void
318
_remove_actions_for_type (NleComposition * comp, GCallback callback)
319
{
320
  GList *tmp;
321

322
  ACTIONS_LOCK (comp);
323
324
325
326
327

  GST_LOG_OBJECT (comp, "finding action[callback=%s], action count = %d",
      GST_DEBUG_FUNCPTR_NAME (callback), g_list_length (comp->priv->actions));
  tmp = g_list_first (comp->priv->actions);
  while (tmp != NULL) {
328
    Action *act = tmp->data;
329
    GList *removed = NULL;
330
331

    if (ACTION_CALLBACK (act) == callback) {
332
333
334
      GST_LOG_OBJECT (comp, "remove action for callback %s",
          GST_DEBUG_FUNCPTR_NAME (callback));
      removed = tmp;
335
      g_closure_unref ((GClosure *) act);
336
      comp->priv->actions = g_list_remove_link (comp->priv->actions, removed);
337
    }
338
339
340
341

    tmp = g_list_next (tmp);
    if (removed)
      g_list_free (removed);
342
  }
343

344
  ACTIONS_UNLOCK (comp);
345
346
}

347
static void
348
_execute_actions (NleComposition * comp)
349
{
350
351
352
353
  NleCompositionPrivate *priv = comp->priv;

  ACTIONS_LOCK (comp);
  if (priv->running == FALSE) {
354
355
    GST_DEBUG_OBJECT (comp, "Not running anymore");

356
    ACTIONS_UNLOCK (comp);
357
358
359
    return;
  }

360
361
362
363
364
365
366
367
368
369
370
371
372
373
  if (priv->actions == NULL)
    WAIT_FOR_AN_ACTION (comp);

  if (comp->priv->running == FALSE) {
    GST_INFO_OBJECT (comp, "Done waiting but not running anymore");

    ACTIONS_UNLOCK (comp);
    return;
  }

  if (priv->actions) {
    GValue params[1] = { G_VALUE_INIT };
    GList *lact;

374
375
376
    GST_LOG_OBJECT (comp, "scheduled actions [%d]",
        g_list_length (priv->actions));

377
378
379
    g_value_init (&params[0], G_TYPE_OBJECT);
    g_value_set_object (&params[0], comp);

380
381
    lact = g_list_first (priv->actions);
    priv->actions = g_list_remove_link (priv->actions, lact);
382
383
384
385
386
    ACTIONS_UNLOCK (comp);

    GST_INFO_OBJECT (comp, "Invoking %p:%s",
        lact->data, GST_DEBUG_FUNCPTR_NAME ((ACTION_CALLBACK (lact->data))));
    g_closure_invoke (lact->data, NULL, 1, params, NULL);
387
    g_value_unset (&params[0]);
388
389
390
391
392
    g_closure_unref (lact->data);
    g_list_free (lact);

    GST_LOG_OBJECT (comp, "remaining actions [%d]",
        g_list_length (priv->actions));
393
394
395
  } else {
    ACTIONS_UNLOCK (comp);
  }
396
397
398
}

static void
399
_start_task (NleComposition * comp)
400
{
401
  GstTask *task;
402

403
  ACTIONS_LOCK (comp);
404
  comp->priv->running = TRUE;
405
  ACTIONS_UNLOCK (comp);
406
407
408
409
410

  GST_OBJECT_LOCK (comp);

  task = comp->task;
  if (task == NULL) {
411
412
413
    gchar *taskname =
        g_strdup_printf ("%s_update_management", GST_OBJECT_NAME (comp));

414
    task = gst_task_new ((GstTaskFunction) _execute_actions, comp, NULL);
415
    gst_object_set_name (GST_OBJECT_CAST (task), taskname);
416
    gst_task_set_lock (task, GET_TASK_LOCK (comp));
417
    GST_DEBUG_OBJECT (comp, "created task %p", task);
418
    comp->task = task;
419
    g_free (taskname);
420
421
422
423
  }

  gst_task_set_state (task, GST_TASK_STARTED);
  GST_OBJECT_UNLOCK (comp);
424
425
426
}

static gboolean
427
_stop_task (NleComposition * comp)
428
429
{
  gboolean res = TRUE;
430
  GstTask *task;
431

432
  GST_INFO_OBJECT (comp, "Stoping children management task");
433

434
  ACTIONS_LOCK (comp);
435
436
  comp->priv->running = FALSE;

437
438
439
  /*  Make sure we do not stay blocked trying to execute an action */
  SIGNAL_NEW_ACTION (comp);
  ACTIONS_UNLOCK (comp);
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
  GST_DEBUG_OBJECT (comp, "stop task");

  GST_OBJECT_LOCK (comp);
  task = comp->task;
  if (task == NULL)
    goto no_task;
  comp->task = NULL;
  res = gst_task_set_state (task, GST_TASK_STOPPED);
  GST_OBJECT_UNLOCK (comp);

  if (!gst_task_join (task))
    goto join_failed;

  gst_object_unref (task);

  return res;

no_task:
  {
    GST_OBJECT_UNLOCK (comp);

    /* this is not an error */
    return TRUE;
  }
join_failed:
  {
    /* this is bad, possibly the application tried to join the task from
     * the task's thread. We install the task again so that it will be stopped
     * again from the right thread next time hopefully. */
    GST_OBJECT_LOCK (comp);
    GST_DEBUG_OBJECT (comp, "join failed");
    /* we can only install this task if there was no other task */
    if (comp->task == NULL)
      comp->task = task;
    GST_OBJECT_UNLOCK (comp);

    return FALSE;
  }
479
480
481
482

  return res;
}

483
static void
484
485
_post_start_composition_update (NleComposition * comp,
    gint32 seqnum, NleUpdateStackReason reason)
486
487
488
489
{
  GstMessage *msg;

  msg = gst_message_new_element (GST_OBJECT (comp),
490
      gst_structure_new ("NleCompositionStartUpdate",
491
492
493
494
495
496
497
          "reason", G_TYPE_STRING, UPDATE_PIPELINE_REASONS[reason], NULL));

  gst_message_set_seqnum (msg, seqnum);
  gst_element_post_message (GST_ELEMENT (comp), msg);
}

static void
498
499
_post_start_composition_update_done (NleComposition * comp,
    gint32 seqnum, NleUpdateStackReason reason)
500
501
{
  GstMessage *msg = gst_message_new_element (GST_OBJECT (comp),
502
      gst_structure_new ("NleCompositionUpdateDone",
503
504
505
506
507
508
509
          "reason", G_TYPE_STRING, UPDATE_PIPELINE_REASONS[reason],
          NULL));

  gst_message_set_seqnum (msg, seqnum);
  gst_element_post_message (GST_ELEMENT (comp), msg);
}

510
static void
511
_seek_pipeline_func (NleComposition * comp, SeekData * seekd)
512
513
514
515
516
517
{
  gdouble rate;
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  gint64 cur, stop;
518
  NleCompositionPrivate *priv = comp->priv;
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534

  gst_event_parse_seek (seekd->event, &rate, &format, &flags,
      &cur_type, &cur, &stop_type, &stop);

  GST_DEBUG_OBJECT (seekd->comp,
      "start:%" GST_TIME_FORMAT " -- stop:%" GST_TIME_FORMAT "  flags:%d",
      GST_TIME_ARGS (cur), GST_TIME_ARGS (stop), flags);

  gst_segment_do_seek (priv->segment,
      rate, format, flags, cur_type, cur, stop_type, stop, NULL);
  gst_segment_do_seek (priv->outside_segment,
      rate, format, flags, cur_type, cur, stop_type, stop, NULL);

  GST_DEBUG_OBJECT (seekd->comp, "Segment now has flags:%d",
      priv->segment->flags);

535
  if (priv->segment->start >= NLE_OBJECT_STOP (seekd->comp)) {
536
537
538
    GST_INFO_OBJECT (seekd->comp,
        "Start %" GST_TIME_FORMAT " > comp->stop: %" GST_TIME_FORMAT
        " Not seeking", GST_TIME_ARGS (priv->segment->start),
539
        GST_TIME_ARGS (NLE_OBJECT_STOP (seekd->comp)));
540
    GST_FIXME_OBJECT (seekd->comp, "HANDLE error async!");
541
    return;
542
543
  }

544
545
546
  _post_start_composition_update (seekd->comp,
      gst_event_get_seqnum (seekd->event), COMP_UPDATE_STACK_ON_SEEK);

547
548
549
550
  /* crop the segment start/stop values */
  /* Only crop segment start value if we don't have a default object */
  if (priv->expandables == NULL)
    priv->segment->start =
551
        MAX (priv->segment->start, NLE_OBJECT_START (seekd->comp));
552
  priv->segment->stop =
553
      MIN (priv->segment->stop, NLE_OBJECT_STOP (seekd->comp));
554
555
556

  priv->next_base_time = 0;

557
558
559
560
561
  seek_handling (seekd->comp, gst_event_get_seqnum (seekd->event),
      COMP_UPDATE_STACK_ON_SEEK);

  _post_start_composition_update_done (seekd->comp,
      gst_event_get_seqnum (seekd->event), COMP_UPDATE_STACK_ON_SEEK);
562
563
}

564
565
/*  Must be called with OBJECTS_LOCK taken */
static void
566
_process_pending_entries (NleComposition * comp)
567
{
568
  NleObject *object;
569
570
571
  GHashTableIter iter;
  gboolean deactivated_stack = FALSE;

572
  NleCompositionPrivate *priv = comp->priv;
573
574
575
576
577
578
579
580
581
582
583
584

  g_hash_table_iter_init (&iter, priv->pending_io);
  while (g_hash_table_iter_next (&iter, (gpointer *) & object, NULL)) {
    if (g_hash_table_contains (priv->objects_hash, object)) {

      if (GST_OBJECT_PARENT (object) == GST_OBJECT_CAST (priv->current_bin) &&
          deactivated_stack == FALSE) {
        deactivated_stack = TRUE;

        _deactivate_stack (comp, TRUE);
      }

585
      _nle_composition_remove_object (comp, object);
586
    } else {
587
      _nle_composition_add_object (comp, object);
588
589
590
591
592
593
594
595
    }
  }

  g_hash_table_remove_all (priv->pending_io);
}


static inline gboolean
596
_commit_values (NleComposition * comp)
597
598
599
{
  GList *tmp;
  gboolean commited = FALSE;
600
  NleCompositionPrivate *priv = comp->priv;
601
602

  for (tmp = priv->objects_start; tmp; tmp = tmp->next) {
603
    if (nle_object_commit (tmp->data, TRUE))
604
605
606
607
      commited = TRUE;
  }

  GST_DEBUG_OBJECT (comp, "Linking up commit vmethod");
608
  commited |= NLE_OBJECT_CLASS (parent_class)->commit (NLE_OBJECT (comp), TRUE);
609
610
611
612
613

  return commited;
}

static gboolean
614
_commit_all_values (NleComposition * comp)
615
{
616
  NleCompositionPrivate *priv = comp->priv;
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634

  priv->next_base_time = 0;

  _process_pending_entries (comp);

  if (_commit_values (comp) == FALSE) {

    return FALSE;;
  }

  /* The topology of the composition might have changed, update the lists */
  priv->objects_start = g_list_sort
      (priv->objects_start, (GCompareFunc) objects_start_compare);
  priv->objects_stop = g_list_sort
      (priv->objects_stop, (GCompareFunc) objects_stop_compare);

  return TRUE;
}
635

636
static gboolean
637
_initialize_stack_func (NleComposition * comp, UpdateCompositionData * ucompo)
638
{
639
  NleCompositionPrivate *priv = comp->priv;
640

641

642
  _post_start_composition_update (comp, ucompo->seqnum, ucompo->reason);
643
644
645

  _commit_all_values (comp);
  update_start_stop_duration (comp);
646
  comp->priv->next_base_time = 0;
647
  /* set ghostpad target */
648
  if (!(update_pipeline (comp, COMP_REAL_START (comp),
649
              ucompo->seqnum, COMP_UPDATE_STACK_INITIALIZE))) {
650
651
652
    GST_FIXME_OBJECT (comp, "PLEASE signal state change failure ASYNC");
  }

653
  _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason);
654
655
656
657
658
  priv->initialized = TRUE;

  return G_SOURCE_REMOVE;
}

659
static void
660
_remove_object_func (NleComposition * comp, ChildIOData * childio)
661
{
662
  NleObject *object = childio->object;
663

664
665
  NleCompositionPrivate *priv = comp->priv;
  NleObject *in_pending_io;
666
667
668

  in_pending_io = g_hash_table_lookup (priv->pending_io, object);

669
  if (!g_hash_table_contains (priv->objects_hash, object)) {
670
671
672
673
674
    if (in_pending_io) {
      GST_INFO_OBJECT (comp, "Object %" GST_PTR_FORMAT " was marked"
          " for addition, removing it from the addition list", object);

      g_hash_table_remove (priv->pending_io, object);
675
      return;
676
677
678
679
680
    }

    GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is "
        " not in the composition", object);

681
    return;
682
683
684
685
686
687
  }

  if (in_pending_io) {
    GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked"
        " for removal", object);

688
    return;
689
690
691
692
  }

  g_hash_table_add (priv->pending_io, object);

693
  return;
694
695
696
}

static void
697
_add_remove_object_action (NleComposition * comp, NleObject * object)
698
699
700
{
  ChildIOData *childio = g_slice_new0 (ChildIOData);

701
  GST_DEBUG_OBJECT (comp, "Adding Action");
702

703
704
705
  childio->comp = comp;
  childio->object = object;

706
707
  _add_action (comp, G_CALLBACK (_remove_object_func),
      childio, G_PRIORITY_DEFAULT);
708
709
}

710
711
static void
_add_object_func (NleComposition * comp, ChildIOData * childio)
712
{
713
714
715
  NleObject *object = childio->object;
  NleCompositionPrivate *priv = comp->priv;
  NleObject *in_pending_io;
716
717
718

  in_pending_io = g_hash_table_lookup (priv->pending_io, object);

719
  if (g_hash_table_contains (priv->objects_hash, object)) {
720
721
722
723
724
725
726
727

    if (in_pending_io) {
      GST_INFO_OBJECT (comp, "Object already in but marked in pendings"
          " removing from pendings");
      g_hash_table_remove (priv->pending_io, object);

      return;
    }
728
729
    GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is "
        " already in the composition", object);
730

731
    return;
732
733
734
735
736
  }

  if (in_pending_io) {
    GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked"
        " for addition", object);
737

738
    return;
739
740
741
  }

  g_hash_table_add (priv->pending_io, object);
742
743
744
}

static void
745
_add_add_object_action (NleComposition * comp, NleObject * object)
746
747
748
{
  ChildIOData *childio = g_slice_new0 (ChildIOData);

749
  GST_DEBUG_OBJECT (comp, "Adding Action");
750

751
752
753
  childio->comp = comp;
  childio->object = object;

754
755
  _add_action (comp, G_CALLBACK (_add_object_func), childio,
      G_PRIORITY_DEFAULT);
756
757
}

758
static void
759
_free_action (gpointer udata, Action * action)
760
{
761
762
  GST_LOG ("freeing %p action for %s", action,
      GST_DEBUG_FUNCPTR_NAME (ACTION_CALLBACK (action)));
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
  if (ACTION_CALLBACK (action) == _seek_pipeline_func) {
    SeekData *seekd = (SeekData *) udata;

    gst_event_unref (seekd->event);
    g_slice_free (SeekData, seekd);
  } else if (ACTION_CALLBACK (action) == _remove_object_func ||
      ACTION_CALLBACK (action) == _add_object_func) {
    g_slice_free (ChildIOData, udata);
  } else if (ACTION_CALLBACK (action) == _update_pipeline_func ||
      ACTION_CALLBACK (action) == _commit_func ||
      ACTION_CALLBACK (action) == _initialize_stack_func) {
    g_slice_free (UpdateCompositionData, udata);
  }
}

static void
_add_action (NleComposition * comp, GCallback func,
    gpointer data, gint priority)
{
  Action *action;
  NleCompositionPrivate *priv = comp->priv;

  action = (Action *) g_closure_new_simple (sizeof (Action), data);
  g_closure_add_finalize_notifier ((GClosure *) action, data,
      (GClosureNotify) _free_action);
  ACTION_CALLBACK (action) = func;
789

790
791
792
793
794
795
796
797
798
799
800
801
  action->priority = priority;
  g_closure_set_marshal ((GClosure *) action, g_cclosure_marshal_VOID__VOID);

  ACTIONS_LOCK (comp);
  GST_INFO_OBJECT (comp, "Adding Action for function: %p:%s",
      action, GST_DEBUG_FUNCPTR_NAME (func));

  if (func == G_CALLBACK (_emit_commited_signal_func))
    priv->actions = g_list_prepend (priv->actions, action);
  else
    priv->actions = g_list_append (priv->actions, action);

802
803
804
  GST_LOG_OBJECT (comp, "the number of remaining actions: %d",
      g_list_length (priv->actions));

805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
  SIGNAL_NEW_ACTION (comp);
  ACTIONS_UNLOCK (comp);
}

static void
_add_seek_action (NleComposition * comp, GstEvent * event)
{
  SeekData *seekd = g_slice_new0 (SeekData);

  GST_DEBUG_OBJECT (comp, "Adding Action");

  seekd->comp = comp;
  seekd->event = event;

  comp->priv->next_eos_seqnum = 0;
  comp->priv->real_eos_seqnum = 0;
  _add_action (comp, G_CALLBACK (_seek_pipeline_func), seekd,
      G_PRIORITY_DEFAULT);
}

static void
_remove_update_actions (NleComposition * comp)
{
  _remove_actions_for_type (comp, G_CALLBACK (_update_pipeline_func));
}

static void
_remove_seek_actions (NleComposition * comp)
{
  _remove_actions_for_type (comp, G_CALLBACK (_seek_pipeline_func));
}

static void
_add_update_compo_action (NleComposition * comp,
    GCallback callback, NleUpdateStackReason reason)
840
841
842
843
844
845
846
847
848
849
{
  UpdateCompositionData *ucompo = g_slice_new0 (UpdateCompositionData);

  ucompo->comp = comp;
  ucompo->reason = reason;
  ucompo->seqnum = gst_util_seqnum_next ();

  GST_INFO_OBJECT (comp, "Updating because: %s -- Setting seqnum: %i",
      UPDATE_PIPELINE_REASONS[reason], ucompo->seqnum);

850
  _add_action (comp, callback, ucompo, G_PRIORITY_DEFAULT);
851
852
}

853
static void
854
nle_composition_handle_message (GstBin * bin, GstMessage * message)
855
{
856
  NleComposition *comp = (NleComposition *) bin;
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874

  if (comp->priv->tearing_down_stack) {
    if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
      GST_FIXME_OBJECT (comp, "Dropping %" GST_PTR_FORMAT " message from "
          " %" GST_PTR_FORMAT " being teared down to READY",
          message, GST_MESSAGE_SRC (message));
    }

    GST_DEBUG_OBJECT (comp, "Dropping message %" GST_PTR_FORMAT " from "
        "object being teared down to READY!", message);
    gst_message_unref (message);

    return;
  }

  GST_BIN_CLASS (parent_class)->handle_message (bin, message);
}

875
static void
876
nle_composition_class_init (NleCompositionClass * klass)
877
878
879
880
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
  GstBinClass *gstbin_class;
881
  NleObjectClass *nleobject_class;
882
883
884
885

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
  gstbin_class = (GstBinClass *) klass;
886
  nleobject_class = (NleObjectClass *) klass;
887

888
  g_type_class_add_private (klass, sizeof (NleCompositionPrivate));
889
890

  gst_element_class_set_static_metadata (gstelement_class,
891
      "GNonLin Composition", "Filter/Editor", "Combines NLE objects",
892
893
894
      "Wim Taymans <wim.taymans@gmail.com>, Edward Hervey <bilboed@bilboed.com>,"
      " Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>,"
      " Thibault Saunier <tsaunier@gnome.org>");
895

896
897
  gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_composition_dispose);
  gobject_class->finalize = GST_DEBUG_FUNCPTR (nle_composition_finalize);
898

899
  gstelement_class->change_state = nle_composition_change_state;
900

901
  gstbin_class->add_element = GST_DEBUG_FUNCPTR (nle_composition_add_object);
902
  gstbin_class->remove_element =
903
      GST_DEBUG_FUNCPTR (nle_composition_remove_object);
904
  gstbin_class->handle_message =
905
      GST_DEBUG_FUNCPTR (nle_composition_handle_message);
906
907

  gst_element_class_add_pad_template (gstelement_class,
908
      gst_static_pad_template_get (&nle_composition_src_template));
909

910
  /* Get the paramspec of the NleObject klass so we can do
911
   * fast notifies */
912
  nleobject_properties[NLEOBJECT_PROP_START] =
913
      g_object_class_find_property (gobject_class, "start");
914
  nleobject_properties[NLEOBJECT_PROP_STOP] =
915
      g_object_class_find_property (gobject_class, "stop");
916
  nleobject_properties[NLEOBJECT_PROP_DURATION] =
917
918
      g_object_class_find_property (gobject_class, "duration");

Thibault Saunier's avatar
Thibault Saunier committed
919
920
921
922
  _signals[COMMITED_SIGNAL] =
      g_signal_new ("commited", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
      0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
      G_TYPE_BOOLEAN);
923

924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
  /**
   * NleComposition::query-position
   * @comp: a #NleComposition
   *
   * A signal that *has* to be connected and which should return the current
   * position of the pipeline.
   *
   * This signal is used in order to know the current position of the whole
   * pipeline so it is user's responsability to give that answer as there
   * is no other way to precisely know the position in the whole pipeline.
   */
  _signals[QUERY_POSITION_SIGNAL] =
      g_signal_new ("query-position", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
      0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_UINT64, 0, NULL);

940
941
942
943
944
945
946
  GST_DEBUG_REGISTER_FUNCPTR (_seek_pipeline_func);
  GST_DEBUG_REGISTER_FUNCPTR (_remove_object_func);
  GST_DEBUG_REGISTER_FUNCPTR (_add_object_func);
  GST_DEBUG_REGISTER_FUNCPTR (_update_pipeline_func);
  GST_DEBUG_REGISTER_FUNCPTR (_commit_func);
  GST_DEBUG_REGISTER_FUNCPTR (_emit_commited_signal_func);
  GST_DEBUG_REGISTER_FUNCPTR (_initialize_stack_func);
947
948
949

  /* Just be useless, so the compiler does not warn us
   * about our uselessness */
950
  nleobject_class->commit = nle_composition_commit_func;
951

952
953
954
}

static void
955
nle_composition_init (NleComposition * comp)
956
{
957
  NleCompositionPrivate *priv;
958

959
960
  GST_OBJECT_FLAG_SET (comp, NLE_OBJECT_SOURCE);
  GST_OBJECT_FLAG_SET (comp, NLE_OBJECT_COMPOSITION);
961

962
963
  priv = G_TYPE_INSTANCE_GET_PRIVATE (comp, NLE_TYPE_COMPOSITION,
      NleCompositionPrivate);
964
965
966
967
968
969
  priv->objects_start = NULL;
  priv->objects_stop = NULL;

  priv->segment = gst_segment_new ();
  priv->outside_segment = gst_segment_new ();

970
971
  g_rec_mutex_init (&comp->task_rec_lock);

972
  priv->objects_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
973

974
975
  g_mutex_init (&priv->actions_lock);
  g_cond_init (&priv->actions_cond);
976
977

  priv->pending_io = g_hash_table_new (g_direct_hash, g_direct_equal);
978
979
980

  comp->priv = priv;

981
982
983
  priv->current_bin = gst_bin_new ("current-bin");
  gst_bin_add (GST_BIN (comp), priv->current_bin);

984
  nle_composition_reset (comp);
985

986
987
988
  priv->nle_event_pad_func = GST_PAD_EVENTFUNC (NLE_OBJECT_SRC (comp));
  gst_pad_set_event_function (NLE_OBJECT_SRC (comp),
      GST_DEBUG_FUNCPTR (nle_composition_event_handler));
989
990
}

991
992
993
994
995
996
997
998
999
1000
static void
_remove_each_nleobj (gpointer data, gpointer udata)
{
  NleComposition *comp = NLE_COMPOSITION (udata);
  NleObject *nleobj = NLE_OBJECT (data);

  _nle_composition_remove_object (NLE_COMPOSITION (comp), NLE_OBJECT (nleobj));
}

static void
For faster browsing, not all history is shown. View entire blame