gnlghostpad: Do not forget to set output segment seqnum
[platform/upstream/gstreamer.git] / gnl / gnlghostpad.c
1 /* Gnonlin
2  * Copyright (C) <2009> Edward Hervey <bilboed@bilboed.com>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "gnl.h"
25
26 GST_DEBUG_CATEGORY_STATIC (gnlghostpad);
27 #define GST_CAT_DEFAULT gnlghostpad
28
29 typedef struct _GnlPadPrivate GnlPadPrivate;
30
31 struct _GnlPadPrivate
32 {
33   GnlObject *object;
34   GnlPadPrivate *ghostpriv;
35   GstPadDirection dir;
36   GstPadEventFunction eventfunc;
37   GstPadQueryFunction queryfunc;
38
39   GstEvent *pending_seek;
40 };
41
42 GstEvent *
43 gnl_object_translate_incoming_seek (GnlObject * object, GstEvent * event)
44 {
45   GstEvent *event2;
46   GstFormat format;
47   gdouble rate;
48   GstSeekFlags flags;
49   GstSeekType curtype, stoptype;
50   GstSeekType ncurtype;
51   gint64 cur;
52   guint64 ncur;
53   gint64 stop;
54   guint64 nstop;
55   guint32 seqnum = GST_EVENT_SEQNUM (event);
56
57   gst_event_parse_seek (event, &rate, &format, &flags,
58       &curtype, &cur, &stoptype, &stop);
59
60   GST_DEBUG_OBJECT (object,
61       "GOT SEEK rate:%f, format:%d, flags:%d, curtype:%d, stoptype:%d, %"
62       GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, format, flags, curtype,
63       stoptype, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop));
64
65   if (G_UNLIKELY (format != GST_FORMAT_TIME))
66     goto invalid_format;
67
68   /* convert cur */
69   ncurtype = GST_SEEK_TYPE_SET;
70   if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET)
71           && (gnl_object_to_media_time (object, cur, &ncur)))) {
72     /* cur is TYPE_SET and value is valid */
73     if (ncur > G_MAXINT64)
74       GST_WARNING_OBJECT (object, "return value too big...");
75     GST_LOG_OBJECT (object, "Setting cur to %" GST_TIME_FORMAT,
76         GST_TIME_ARGS (ncur));
77   } else if ((curtype != GST_SEEK_TYPE_NONE)) {
78     GST_DEBUG_OBJECT (object, "Limiting seek start to inpoint");
79     ncur = object->inpoint;
80   } else {
81     GST_DEBUG_OBJECT (object, "leaving GST_SEEK_TYPE_NONE");
82     ncur = cur;
83     ncurtype = GST_SEEK_TYPE_NONE;
84   }
85
86   /* convert stop, we also need to limit it to object->stop */
87   if (G_LIKELY ((stoptype == GST_SEEK_TYPE_SET)
88           && (gnl_object_to_media_time (object, stop, &nstop)))) {
89     if (nstop > G_MAXINT64)
90       GST_WARNING_OBJECT (object, "return value too big...");
91     GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
92         GST_TIME_ARGS (nstop));
93   } else {
94     GST_DEBUG_OBJECT (object, "Limiting end of seek to media_stop");
95     gnl_object_to_media_time (object, object->stop, &nstop);
96     if (nstop > G_MAXINT64)
97       GST_WARNING_OBJECT (object, "return value too big...");
98     GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
99         GST_TIME_ARGS (nstop));
100   }
101
102
103   /* add accurate seekflags */
104   if (G_UNLIKELY (!(flags & GST_SEEK_FLAG_ACCURATE))) {
105     GST_DEBUG_OBJECT (object, "Adding GST_SEEK_FLAG_ACCURATE");
106     flags |= GST_SEEK_FLAG_ACCURATE;
107   } else {
108     GST_DEBUG_OBJECT (object,
109         "event already has GST_SEEK_FLAG_ACCURATE : %d", flags);
110   }
111
112
113
114   GST_DEBUG_OBJECT (object,
115       "SENDING SEEK rate:%f, format:TIME, flags:%d, curtype:%d, stoptype:SET, %"
116       GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, flags, ncurtype,
117       GST_TIME_ARGS (ncur), GST_TIME_ARGS (nstop));
118
119   event2 = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
120       ncurtype, (gint64) ncur, GST_SEEK_TYPE_SET, (gint64) nstop);
121   GST_EVENT_SEQNUM (event2) = seqnum;
122
123   return event2;
124
125   /* ERRORS */
126 invalid_format:
127   {
128     GST_WARNING ("GNonLin time shifting only works with GST_FORMAT_TIME");
129     return event;
130   }
131 }
132
133 static GstEvent *
134 translate_outgoing_seek (GnlObject * object, GstEvent * event)
135 {
136   GstEvent *event2;
137   GstFormat format;
138   gdouble rate;
139   GstSeekFlags flags;
140   GstSeekType curtype, stoptype;
141   GstSeekType ncurtype;
142   gint64 cur;
143   guint64 ncur;
144   gint64 stop;
145   guint64 nstop;
146   guint32 seqnum = GST_EVENT_SEQNUM (event);
147
148   gst_event_parse_seek (event, &rate, &format, &flags,
149       &curtype, &cur, &stoptype, &stop);
150
151   GST_DEBUG_OBJECT (object,
152       "GOT SEEK rate:%f, format:%d, flags:%d, curtype:%d, stoptype:%d, %"
153       GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, format, flags, curtype,
154       stoptype, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop));
155
156   if (G_UNLIKELY (format != GST_FORMAT_TIME))
157     goto invalid_format;
158
159   /* convert cur */
160   ncurtype = GST_SEEK_TYPE_SET;
161   if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET)
162           && (gnl_media_to_object_time (object, cur, &ncur)))) {
163     /* cur is TYPE_SET and value is valid */
164     if (ncur > G_MAXINT64)
165       GST_WARNING_OBJECT (object, "return value too big...");
166     GST_LOG_OBJECT (object, "Setting cur to %" GST_TIME_FORMAT,
167         GST_TIME_ARGS (ncur));
168   } else if ((curtype != GST_SEEK_TYPE_NONE)) {
169     GST_DEBUG_OBJECT (object, "Limiting seek start to start");
170     ncur = object->start;
171   } else {
172     GST_DEBUG_OBJECT (object, "leaving GST_SEEK_TYPE_NONE");
173     ncur = cur;
174     ncurtype = GST_SEEK_TYPE_NONE;
175   }
176
177   /* convert stop, we also need to limit it to object->stop */
178   if (G_LIKELY ((stoptype == GST_SEEK_TYPE_SET)
179           && (gnl_media_to_object_time (object, stop, &nstop)))) {
180     if (nstop > G_MAXINT64)
181       GST_WARNING_OBJECT (object, "return value too big...");
182     GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
183         GST_TIME_ARGS (nstop));
184   } else {
185     GST_DEBUG_OBJECT (object, "Limiting end of seek to stop");
186     nstop = object->stop;
187     if (nstop > G_MAXINT64)
188       GST_WARNING_OBJECT (object, "return value too big...");
189     GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
190         GST_TIME_ARGS (nstop));
191   }
192
193   GST_DEBUG_OBJECT (object,
194       "SENDING SEEK rate:%f, format:TIME, flags:%d, curtype:%d, stoptype:SET, %"
195       GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, flags, ncurtype,
196       GST_TIME_ARGS (ncur), GST_TIME_ARGS (nstop));
197
198   event2 = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
199       ncurtype, (gint64) ncur, GST_SEEK_TYPE_SET, (gint64) nstop);
200   GST_EVENT_SEQNUM (event2) = seqnum;
201
202   gst_event_unref (event);
203
204   return event2;
205
206   /* ERRORS */
207 invalid_format:
208   {
209     GST_WARNING ("GNonLin time shifting only works with GST_FORMAT_TIME");
210     return event;
211   }
212 }
213
214 static GstEvent *
215 translate_outgoing_segment (GnlObject * object, GstEvent * event)
216 {
217   const GstSegment *orig;
218   GstSegment segment;
219   GstEvent *event2;
220   guint32 seqnum = GST_EVENT_SEQNUM (event);
221
222   /* only modify the streamtime */
223   gst_event_parse_segment (event, &orig);
224
225   GST_DEBUG_OBJECT (object,
226       "Got SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
227       GST_TIME_FORMAT, GST_TIME_ARGS (orig->start), GST_TIME_ARGS (orig->stop),
228       GST_TIME_ARGS (orig->time));
229
230   if (G_UNLIKELY (orig->format != GST_FORMAT_TIME)) {
231     GST_WARNING_OBJECT (object,
232         "Can't translate segments with format != GST_FORMAT_TIME");
233     return event;
234   }
235
236   gst_segment_copy_into (orig, &segment);
237
238   gnl_media_to_object_time (object, orig->time, &segment.time);
239
240   if (G_UNLIKELY (segment.time > G_MAXINT64))
241     GST_WARNING_OBJECT (object, "Return value too big...");
242
243   GST_DEBUG_OBJECT (object,
244       "Sending SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
245       GST_TIME_FORMAT, GST_TIME_ARGS (segment.start),
246       GST_TIME_ARGS (segment.stop), GST_TIME_ARGS (segment.time));
247
248   event2 = gst_event_new_segment (&segment);
249   GST_EVENT_SEQNUM (event2) = seqnum;
250   gst_event_unref (event);
251
252   return event2;
253 }
254
255 static GstEvent *
256 translate_incoming_segment (GnlObject * object, GstEvent * event)
257 {
258   GstEvent *event2;
259   const GstSegment *orig;
260   GstSegment segment;
261   guint32 seqnum = GST_EVENT_SEQNUM (event);
262
263   /* only modify the streamtime */
264   gst_event_parse_segment (event, &orig);
265
266   GST_DEBUG_OBJECT (object,
267       "Got SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
268       GST_TIME_FORMAT, GST_TIME_ARGS (orig->start), GST_TIME_ARGS (orig->stop),
269       GST_TIME_ARGS (orig->time));
270
271   if (G_UNLIKELY (orig->format != GST_FORMAT_TIME)) {
272     GST_WARNING_OBJECT (object,
273         "Can't translate segments with format != GST_FORMAT_TIME");
274     return event;
275   }
276
277   gst_segment_copy_into (orig, &segment);
278
279   if (!gnl_object_to_media_time (object, orig->time, &segment.time)) {
280     GST_DEBUG ("Can't convert media_time, using 0");
281     segment.time = 0;
282   };
283
284   if (GNL_IS_OPERATION (object)) {
285     segment.base = GNL_OPERATION (object)->next_base_time;
286     GST_INFO_OBJECT (object, "Using operation base time %" GST_TIME_FORMAT,
287         GST_TIME_ARGS (GNL_OPERATION (object)->next_base_time));
288   }
289
290   if (G_UNLIKELY (segment.time > G_MAXINT64))
291     GST_WARNING_OBJECT (object, "Return value too big...");
292
293   GST_DEBUG_OBJECT (object,
294       "Sending SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
295       GST_TIME_FORMAT, GST_TIME_ARGS (segment.start),
296       GST_TIME_ARGS (segment.stop), GST_TIME_ARGS (segment.time));
297
298   event2 = gst_event_new_segment (&segment);
299   GST_EVENT_SEQNUM (event2) = seqnum;
300   gst_event_unref (event);
301
302   if (object->seqnum) {
303     gst_event_set_seqnum (event, object->seqnum);
304   }
305
306   return event2;
307 }
308
309 static gboolean
310 internalpad_event_function (GstPad * internal, GstObject * parent,
311     GstEvent * event)
312 {
313   GnlPadPrivate *priv = gst_pad_get_element_private (internal);
314   GnlObject *object = priv->object;
315   gboolean res;
316
317   GST_DEBUG_OBJECT (internal, "event:%s (seqnum::%d)",
318       GST_EVENT_TYPE_NAME (event), GST_EVENT_SEQNUM (event));
319
320   if (G_UNLIKELY (!(priv->eventfunc))) {
321     GST_WARNING_OBJECT (internal,
322         "priv->eventfunc == NULL !! What is going on ?");
323     return FALSE;
324   }
325
326   switch (priv->dir) {
327     case GST_PAD_SRC:{
328       switch (GST_EVENT_TYPE (event)) {
329         case GST_EVENT_SEEK:
330           object->wanted_seqnum = gst_event_get_seqnum (event);
331           object->seqnum = 0;
332           GST_DEBUG_OBJECT (object, "Setting wanted_seqnum to %i",
333               object->wanted_seqnum);
334           break;
335         case GST_EVENT_SEGMENT:
336           if (object->wanted_seqnum == 0) {
337             g_assert ("All gnlobject should be seeked at one point or another"
338                 " and thus we should always have a wanted_seqnum when getting"
339                 " a new segment" == NULL);
340           }
341
342           GST_DEBUG_OBJECT (object, "Got segment, seqnum-> %i (wanted %i)",
343               gst_event_get_seqnum (event), object->wanted_seqnum);
344
345           object->seqnum = object->wanted_seqnum;
346           object->wanted_seqnum = 0;
347
348           event = translate_outgoing_segment (object, event);
349           gst_event_set_seqnum (event, object->seqnum);
350           break;
351         case GST_EVENT_EOS:
352           if (object->seqnum)
353             gst_event_set_seqnum (event, object->seqnum);
354           GST_INFO_OBJECT (object, "Tweaking seqnum to %i", object->seqnum);
355           break;
356         default:
357           break;
358       }
359
360       break;
361     }
362     case GST_PAD_SINK:{
363       switch (GST_EVENT_TYPE (event)) {
364         case GST_EVENT_SEEK:
365           event = translate_outgoing_seek (object, event);
366           break;
367         default:
368           break;
369       }
370       break;
371     }
372     default:
373       break;
374   }
375   GST_DEBUG_OBJECT (internal, "Calling priv->eventfunc %p", priv->eventfunc);
376   res = priv->eventfunc (internal, parent, event);
377
378   return res;
379 }
380
381 /*
382   translate_outgoing_position_query
383
384   Should only be called:
385   _ if the query is a GST_QUERY_POSITION
386   _ after the query was sent upstream
387   _ if the upstream query returned TRUE
388 */
389
390 static gboolean
391 translate_incoming_position_query (GnlObject * object, GstQuery * query)
392 {
393   GstFormat format;
394   gint64 cur, cur2;
395
396   gst_query_parse_position (query, &format, &cur);
397   if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
398     GST_WARNING_OBJECT (object,
399         "position query is in a format different from time, returning without modifying values");
400     goto beach;
401   }
402
403   gnl_media_to_object_time (object, (guint64) cur, (guint64 *) & cur2);
404
405   GST_DEBUG_OBJECT (object,
406       "Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
407       GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2));
408   gst_query_set_position (query, GST_FORMAT_TIME, cur2);
409
410 beach:
411   return TRUE;
412 }
413
414 static gboolean
415 translate_outgoing_position_query (GnlObject * object, GstQuery * query)
416 {
417   GstFormat format;
418   gint64 cur, cur2;
419
420   gst_query_parse_position (query, &format, &cur);
421   if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
422     GST_WARNING_OBJECT (object,
423         "position query is in a format different from time, returning without modifying values");
424     goto beach;
425   }
426
427   if (G_UNLIKELY (!(gnl_object_to_media_time (object, (guint64) cur,
428                   (guint64 *) & cur2)))) {
429     GST_WARNING_OBJECT (object,
430         "Couldn't get media time for %" GST_TIME_FORMAT, GST_TIME_ARGS (cur));
431     goto beach;
432   }
433
434   GST_DEBUG_OBJECT (object,
435       "Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
436       GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2));
437   gst_query_set_position (query, GST_FORMAT_TIME, cur2);
438
439 beach:
440   return TRUE;
441 }
442
443 static gboolean
444 translate_incoming_duration_query (GnlObject * object, GstQuery * query)
445 {
446   GstFormat format;
447   gint64 cur;
448
449   gst_query_parse_duration (query, &format, &cur);
450   if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
451     GST_WARNING_OBJECT (object,
452         "We can only handle duration queries in GST_FORMAT_TIME");
453     return FALSE;
454   }
455
456   gst_query_set_duration (query, GST_FORMAT_TIME, object->duration);
457
458   return TRUE;
459 }
460
461 static gboolean
462 internalpad_query_function (GstPad * internal, GstObject * parent,
463     GstQuery * query)
464 {
465   GnlPadPrivate *priv = gst_pad_get_element_private (internal);
466   GnlObject *object = priv->object;
467   gboolean ret;
468
469   GST_DEBUG_OBJECT (internal, "querytype:%s",
470       gst_query_type_get_name (GST_QUERY_TYPE (query)));
471
472   if (!(priv->queryfunc)) {
473     GST_WARNING_OBJECT (internal,
474         "priv->queryfunc == NULL !! What is going on ?");
475     return FALSE;
476   }
477
478   if ((ret = priv->queryfunc (internal, parent, query))) {
479
480     switch (priv->dir) {
481       case GST_PAD_SRC:
482         break;
483       case GST_PAD_SINK:
484         switch (GST_QUERY_TYPE (query)) {
485           case GST_QUERY_POSITION:
486             ret = translate_outgoing_position_query (object, query);
487             break;
488           default:
489             break;
490         }
491         break;
492       default:
493         break;
494     }
495   }
496   return ret;
497 }
498
499 static gboolean
500 ghostpad_event_function (GstPad * ghostpad, GstObject * parent,
501     GstEvent * event)
502 {
503   GnlPadPrivate *priv;
504   GnlObject *object;
505   gboolean ret = FALSE;
506
507   priv = gst_pad_get_element_private (ghostpad);
508   object = priv->object;
509
510   GST_DEBUG_OBJECT (ghostpad, "event:%s", GST_EVENT_TYPE_NAME (event));
511
512   if (G_UNLIKELY (priv->eventfunc == NULL))
513     goto no_function;
514
515   switch (priv->dir) {
516     case GST_PAD_SRC:
517     {
518       switch (GST_EVENT_TYPE (event)) {
519         case GST_EVENT_SEEK:
520         {
521           GstPad *target;
522
523           event = gnl_object_translate_incoming_seek (object, event);
524           object->wanted_seqnum = gst_event_get_seqnum (event);
525           object->seqnum = 0;
526           if (!(target = gst_ghost_pad_get_target (GST_GHOST_PAD (ghostpad)))) {
527             g_assert ("Seeked a pad with not target SHOULD NOT HAPPEND");
528             ret = FALSE;
529             event = NULL;
530           } else {
531             gst_object_unref (target);
532           }
533         }
534           break;
535         default:
536           break;
537       }
538     }
539       break;
540     case GST_PAD_SINK:{
541       switch (GST_EVENT_TYPE (event)) {
542         case GST_EVENT_SEGMENT:
543           event = translate_incoming_segment (object, event);
544           break;
545         default:
546           break;
547       }
548     }
549       break;
550     default:
551       break;
552   }
553
554   if (event) {
555     GST_DEBUG_OBJECT (ghostpad, "Calling priv->eventfunc");
556     ret = priv->eventfunc (ghostpad, parent, event);
557     GST_DEBUG_OBJECT (ghostpad, "Returned from calling priv->eventfunc : %d",
558         ret);
559   }
560
561   return ret;
562
563   /* ERRORS */
564 no_function:
565   {
566     GST_WARNING_OBJECT (ghostpad,
567         "priv->eventfunc == NULL !! What's going on ?");
568     return FALSE;
569   }
570 }
571
572 static gboolean
573 ghostpad_query_function (GstPad * ghostpad, GstObject * parent,
574     GstQuery * query)
575 {
576   GnlPadPrivate *priv = gst_pad_get_element_private (ghostpad);
577   GnlObject *object = GNL_OBJECT (parent);
578   gboolean pret = TRUE;
579
580   GST_DEBUG_OBJECT (ghostpad, "querytype:%s", GST_QUERY_TYPE_NAME (query));
581
582   switch (GST_QUERY_TYPE (query)) {
583     case GST_QUERY_DURATION:
584       /* skip duration upstream query, we'll fill it in ourselves */
585       break;
586     default:
587       pret = priv->queryfunc (ghostpad, parent, query);
588   }
589
590   if (pret) {
591     /* translate result */
592     switch (GST_QUERY_TYPE (query)) {
593       case GST_QUERY_POSITION:
594         pret = translate_incoming_position_query (object, query);
595         break;
596       case GST_QUERY_DURATION:
597         pret = translate_incoming_duration_query (object, query);
598         break;
599       default:
600         break;
601     }
602   }
603
604   return pret;
605 }
606
607 /* internal pad going away */
608 static void
609 internal_pad_finalizing (GnlPadPrivate * priv, GObject * pad G_GNUC_UNUSED)
610 {
611   g_slice_free (GnlPadPrivate, priv);
612 }
613
614 static inline GstPad *
615 get_proxy_pad (GstPad * ghostpad)
616 {
617   GValue item = { 0, };
618   GstIterator *it;
619   GstPad *ret = NULL;
620
621   it = gst_pad_iterate_internal_links (ghostpad);
622   g_assert (it);
623   gst_iterator_next (it, &item);
624   ret = g_value_dup_object (&item);
625   g_value_unset (&item);
626   g_assert (ret);
627   gst_iterator_free (it);
628
629   return ret;
630 }
631
632 static void
633 control_internal_pad (GstPad * ghostpad, GnlObject * object)
634 {
635   GnlPadPrivate *priv;
636   GnlPadPrivate *privghost;
637   GstPad *internal;
638
639   if (!ghostpad) {
640     GST_DEBUG_OBJECT (object, "We don't have a valid ghostpad !");
641     return;
642   }
643   privghost = gst_pad_get_element_private (ghostpad);
644
645   GST_LOG_OBJECT (ghostpad, "overriding ghostpad's internal pad function");
646
647   internal = get_proxy_pad (ghostpad);
648
649   if (G_UNLIKELY (!(priv = gst_pad_get_element_private (internal)))) {
650     GST_DEBUG_OBJECT (internal,
651         "Creating a GnlPadPrivate to put in element_private");
652     priv = g_slice_new0 (GnlPadPrivate);
653
654     /* Remember existing pad functions */
655     priv->eventfunc = GST_PAD_EVENTFUNC (internal);
656     priv->queryfunc = GST_PAD_QUERYFUNC (internal);
657     gst_pad_set_element_private (internal, priv);
658
659     g_object_weak_ref ((GObject *) internal,
660         (GWeakNotify) internal_pad_finalizing, priv);
661
662     /* add query/event function overrides on internal pad */
663     gst_pad_set_event_function (internal,
664         GST_DEBUG_FUNCPTR (internalpad_event_function));
665     gst_pad_set_query_function (internal,
666         GST_DEBUG_FUNCPTR (internalpad_query_function));
667   }
668
669   priv->object = object;
670   priv->ghostpriv = privghost;
671   priv->dir = GST_PAD_DIRECTION (ghostpad);
672   gst_object_unref (internal);
673
674   GST_DEBUG_OBJECT (ghostpad, "Done with pad %s:%s",
675       GST_DEBUG_PAD_NAME (ghostpad));
676 }
677
678
679 /**
680  * gnl_object_ghost_pad:
681  * @object: #GnlObject to add the ghostpad to
682  * @name: Name for the new pad
683  * @target: Target #GstPad to ghost
684  *
685  * Adds a #GstGhostPad overridding the correct pad [query|event]_function so
686  * that time shifting is done correctly
687  * The #GstGhostPad is added to the #GnlObject
688  *
689  * /!\ This function doesn't check if the existing [src|sink] pad was removed
690  * first, so you might end up with more pads than wanted
691  *
692  * Returns: The #GstPad if everything went correctly, else NULL.
693  */
694 GstPad *
695 gnl_object_ghost_pad (GnlObject * object, const gchar * name, GstPad * target)
696 {
697   GstPadDirection dir = GST_PAD_DIRECTION (target);
698   GstPad *ghost;
699
700   GST_DEBUG_OBJECT (object, "name:%s, target:%p", name, target);
701
702   g_return_val_if_fail (target, FALSE);
703   g_return_val_if_fail ((dir != GST_PAD_UNKNOWN), FALSE);
704
705   ghost = gnl_object_ghost_pad_no_target (object, name, dir, NULL);
706   if (!ghost) {
707     GST_WARNING_OBJECT (object, "Couldn't create ghostpad");
708     return NULL;
709   }
710
711   if (!(gnl_object_ghost_pad_set_target (object, ghost, target))) {
712     GST_WARNING_OBJECT (object,
713         "Couldn't set the target pad... removing ghostpad");
714     gst_object_unref (ghost);
715     return NULL;
716   }
717
718   GST_DEBUG_OBJECT (object, "activating ghostpad");
719   /* activate pad */
720   gst_pad_set_active (ghost, TRUE);
721   /* add it to element */
722   if (!(gst_element_add_pad (GST_ELEMENT (object), ghost))) {
723     GST_WARNING ("couldn't add newly created ghostpad");
724     return NULL;
725   }
726
727   return ghost;
728 }
729
730 /*
731  * gnl_object_ghost_pad_no_target:
732  * /!\ Doesn't add the pad to the GnlObject....
733  */
734 GstPad *
735 gnl_object_ghost_pad_no_target (GnlObject * object, const gchar * name,
736     GstPadDirection dir, GstPadTemplate * template)
737 {
738   GstPad *ghost;
739   GnlPadPrivate *priv;
740
741   /* create a no_target ghostpad */
742   if (template)
743     ghost = gst_ghost_pad_new_no_target_from_template (name, template);
744   else
745     ghost = gst_ghost_pad_new_no_target (name, dir);
746   if (!ghost)
747     return NULL;
748
749
750   /* remember the existing ghostpad event/query/link/unlink functions */
751   priv = g_slice_new0 (GnlPadPrivate);
752   priv->dir = dir;
753   priv->object = object;
754
755   /* grab/replace event/query functions */
756   GST_DEBUG_OBJECT (ghost, "Setting priv->eventfunc to %p",
757       GST_PAD_EVENTFUNC (ghost));
758   priv->eventfunc = GST_PAD_EVENTFUNC (ghost);
759   priv->queryfunc = GST_PAD_QUERYFUNC (ghost);
760
761   gst_pad_set_event_function (ghost,
762       GST_DEBUG_FUNCPTR (ghostpad_event_function));
763   gst_pad_set_query_function (ghost,
764       GST_DEBUG_FUNCPTR (ghostpad_query_function));
765
766   gst_pad_set_element_private (ghost, priv);
767   control_internal_pad (ghost, object);
768
769   return ghost;
770 }
771
772
773
774 void
775 gnl_object_remove_ghost_pad (GnlObject * object, GstPad * ghost)
776 {
777   GnlPadPrivate *priv;
778
779   GST_DEBUG_OBJECT (object, "ghostpad %s:%s", GST_DEBUG_PAD_NAME (ghost));
780
781   priv = gst_pad_get_element_private (ghost);
782   gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), NULL);
783   gst_element_remove_pad (GST_ELEMENT (object), ghost);
784   if (priv)
785     g_slice_free (GnlPadPrivate, priv);
786 }
787
788 gboolean
789 gnl_object_ghost_pad_set_target (GnlObject * object, GstPad * ghost,
790     GstPad * target)
791 {
792   GnlPadPrivate *priv = gst_pad_get_element_private (ghost);
793
794   g_return_val_if_fail (priv, FALSE);
795   g_return_val_if_fail (GST_IS_PAD (ghost), FALSE);
796
797   if (target) {
798     GST_DEBUG_OBJECT (object, "setting target %s:%s on %s:%s",
799         GST_DEBUG_PAD_NAME (target), GST_DEBUG_PAD_NAME (ghost));
800   } else {
801     GST_ERROR_OBJECT (object, "removing target from ghostpad");
802     priv->pending_seek = NULL;
803   }
804
805   /* set target */
806   if (!(gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), target))) {
807     GST_WARNING_OBJECT (priv->object, "Could not set ghost %s:%s "
808         "target to: %s:%s", GST_DEBUG_PAD_NAME (ghost),
809         GST_DEBUG_PAD_NAME (target));
810     return FALSE;
811   }
812
813   if (target && priv->pending_seek) {
814     gboolean res = gst_pad_send_event (ghost, priv->pending_seek);
815
816     GST_INFO_OBJECT (object, "Sending our pending seek event: %" GST_PTR_FORMAT
817         " -- Result is %i", priv->pending_seek, res);
818
819     priv->pending_seek = NULL;
820   }
821
822   return TRUE;
823 }
824
825 void
826 gnl_init_ghostpad_category (void)
827 {
828   GST_DEBUG_CATEGORY_INIT (gnlghostpad, "gnlghostpad",
829       GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin GhostPad");
830
831 }