gnlghostpad: Do not tolerate getting seeked when no target is set
[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           break;
350         case GST_EVENT_EOS:
351           if (object->seqnum)
352             gst_event_set_seqnum (event, object->seqnum);
353           GST_INFO_OBJECT (object, "Tweaking seqnum to %i", object->seqnum);
354           break;
355         default:
356           break;
357       }
358
359       break;
360     }
361     case GST_PAD_SINK:{
362       switch (GST_EVENT_TYPE (event)) {
363         case GST_EVENT_SEEK:
364           event = translate_outgoing_seek (object, event);
365           break;
366         default:
367           break;
368       }
369       break;
370     }
371     default:
372       break;
373   }
374   GST_DEBUG_OBJECT (internal, "Calling priv->eventfunc %p", priv->eventfunc);
375   res = priv->eventfunc (internal, parent, event);
376
377   return res;
378 }
379
380 /*
381   translate_outgoing_position_query
382
383   Should only be called:
384   _ if the query is a GST_QUERY_POSITION
385   _ after the query was sent upstream
386   _ if the upstream query returned TRUE
387 */
388
389 static gboolean
390 translate_incoming_position_query (GnlObject * object, GstQuery * query)
391 {
392   GstFormat format;
393   gint64 cur, cur2;
394
395   gst_query_parse_position (query, &format, &cur);
396   if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
397     GST_WARNING_OBJECT (object,
398         "position query is in a format different from time, returning without modifying values");
399     goto beach;
400   }
401
402   gnl_media_to_object_time (object, (guint64) cur, (guint64 *) & cur2);
403
404   GST_DEBUG_OBJECT (object,
405       "Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
406       GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2));
407   gst_query_set_position (query, GST_FORMAT_TIME, cur2);
408
409 beach:
410   return TRUE;
411 }
412
413 static gboolean
414 translate_outgoing_position_query (GnlObject * object, GstQuery * query)
415 {
416   GstFormat format;
417   gint64 cur, cur2;
418
419   gst_query_parse_position (query, &format, &cur);
420   if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
421     GST_WARNING_OBJECT (object,
422         "position query is in a format different from time, returning without modifying values");
423     goto beach;
424   }
425
426   if (G_UNLIKELY (!(gnl_object_to_media_time (object, (guint64) cur,
427                   (guint64 *) & cur2)))) {
428     GST_WARNING_OBJECT (object,
429         "Couldn't get media time for %" GST_TIME_FORMAT, GST_TIME_ARGS (cur));
430     goto beach;
431   }
432
433   GST_DEBUG_OBJECT (object,
434       "Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
435       GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2));
436   gst_query_set_position (query, GST_FORMAT_TIME, cur2);
437
438 beach:
439   return TRUE;
440 }
441
442 static gboolean
443 translate_incoming_duration_query (GnlObject * object, GstQuery * query)
444 {
445   GstFormat format;
446   gint64 cur;
447
448   gst_query_parse_duration (query, &format, &cur);
449   if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
450     GST_WARNING_OBJECT (object,
451         "We can only handle duration queries in GST_FORMAT_TIME");
452     return FALSE;
453   }
454
455   gst_query_set_duration (query, GST_FORMAT_TIME, object->duration);
456
457   return TRUE;
458 }
459
460 static gboolean
461 internalpad_query_function (GstPad * internal, GstObject * parent,
462     GstQuery * query)
463 {
464   GnlPadPrivate *priv = gst_pad_get_element_private (internal);
465   GnlObject *object = priv->object;
466   gboolean ret;
467
468   GST_DEBUG_OBJECT (internal, "querytype:%s",
469       gst_query_type_get_name (GST_QUERY_TYPE (query)));
470
471   if (!(priv->queryfunc)) {
472     GST_WARNING_OBJECT (internal,
473         "priv->queryfunc == NULL !! What is going on ?");
474     return FALSE;
475   }
476
477   if ((ret = priv->queryfunc (internal, parent, query))) {
478
479     switch (priv->dir) {
480       case GST_PAD_SRC:
481         break;
482       case GST_PAD_SINK:
483         switch (GST_QUERY_TYPE (query)) {
484           case GST_QUERY_POSITION:
485             ret = translate_outgoing_position_query (object, query);
486             break;
487           default:
488             break;
489         }
490         break;
491       default:
492         break;
493     }
494   }
495   return ret;
496 }
497
498 static gboolean
499 ghostpad_event_function (GstPad * ghostpad, GstObject * parent,
500     GstEvent * event)
501 {
502   GnlPadPrivate *priv;
503   GnlObject *object;
504   gboolean ret = FALSE;
505
506   priv = gst_pad_get_element_private (ghostpad);
507   object = priv->object;
508
509   GST_DEBUG_OBJECT (ghostpad, "event:%s", GST_EVENT_TYPE_NAME (event));
510
511   if (G_UNLIKELY (priv->eventfunc == NULL))
512     goto no_function;
513
514   switch (priv->dir) {
515     case GST_PAD_SRC:
516     {
517       switch (GST_EVENT_TYPE (event)) {
518         case GST_EVENT_SEEK:
519         {
520           GstPad *target;
521
522           event = gnl_object_translate_incoming_seek (object, event);
523           object->wanted_seqnum = gst_event_get_seqnum (event);
524           object->seqnum = 0;
525           if (!(target = gst_ghost_pad_get_target (GST_GHOST_PAD (ghostpad)))) {
526             g_assert ("Seeked a pad with not target SHOULD NOT HAPPEND");
527             ret = FALSE;
528             event = NULL;
529           } else {
530             gst_object_unref (target);
531           }
532         }
533           break;
534         default:
535           break;
536       }
537     }
538       break;
539     case GST_PAD_SINK:{
540       switch (GST_EVENT_TYPE (event)) {
541         case GST_EVENT_SEGMENT:
542           event = translate_incoming_segment (object, event);
543           break;
544         default:
545           break;
546       }
547     }
548       break;
549     default:
550       break;
551   }
552
553   if (event) {
554     GST_DEBUG_OBJECT (ghostpad, "Calling priv->eventfunc");
555     ret = priv->eventfunc (ghostpad, parent, event);
556     GST_DEBUG_OBJECT (ghostpad, "Returned from calling priv->eventfunc : %d",
557         ret);
558   }
559
560   return ret;
561
562   /* ERRORS */
563 no_function:
564   {
565     GST_WARNING_OBJECT (ghostpad,
566         "priv->eventfunc == NULL !! What's going on ?");
567     return FALSE;
568   }
569 }
570
571 static gboolean
572 ghostpad_query_function (GstPad * ghostpad, GstObject * parent,
573     GstQuery * query)
574 {
575   GnlPadPrivate *priv = gst_pad_get_element_private (ghostpad);
576   GnlObject *object = GNL_OBJECT (parent);
577   gboolean pret = TRUE;
578
579   GST_DEBUG_OBJECT (ghostpad, "querytype:%s", GST_QUERY_TYPE_NAME (query));
580
581   switch (GST_QUERY_TYPE (query)) {
582     case GST_QUERY_DURATION:
583       /* skip duration upstream query, we'll fill it in ourselves */
584       break;
585     default:
586       pret = priv->queryfunc (ghostpad, parent, query);
587   }
588
589   if (pret) {
590     /* translate result */
591     switch (GST_QUERY_TYPE (query)) {
592       case GST_QUERY_POSITION:
593         pret = translate_incoming_position_query (object, query);
594         break;
595       case GST_QUERY_DURATION:
596         pret = translate_incoming_duration_query (object, query);
597         break;
598       default:
599         break;
600     }
601   }
602
603   return pret;
604 }
605
606 /* internal pad going away */
607 static void
608 internal_pad_finalizing (GnlPadPrivate * priv, GObject * pad G_GNUC_UNUSED)
609 {
610   g_slice_free (GnlPadPrivate, priv);
611 }
612
613 static inline GstPad *
614 get_proxy_pad (GstPad * ghostpad)
615 {
616   GValue item = { 0, };
617   GstIterator *it;
618   GstPad *ret = NULL;
619
620   it = gst_pad_iterate_internal_links (ghostpad);
621   g_assert (it);
622   gst_iterator_next (it, &item);
623   ret = g_value_dup_object (&item);
624   g_value_unset (&item);
625   g_assert (ret);
626   gst_iterator_free (it);
627
628   return ret;
629 }
630
631 static void
632 control_internal_pad (GstPad * ghostpad, GnlObject * object)
633 {
634   GnlPadPrivate *priv;
635   GnlPadPrivate *privghost;
636   GstPad *internal;
637
638   if (!ghostpad) {
639     GST_DEBUG_OBJECT (object, "We don't have a valid ghostpad !");
640     return;
641   }
642   privghost = gst_pad_get_element_private (ghostpad);
643
644   GST_LOG_OBJECT (ghostpad, "overriding ghostpad's internal pad function");
645
646   internal = get_proxy_pad (ghostpad);
647
648   if (G_UNLIKELY (!(priv = gst_pad_get_element_private (internal)))) {
649     GST_DEBUG_OBJECT (internal,
650         "Creating a GnlPadPrivate to put in element_private");
651     priv = g_slice_new0 (GnlPadPrivate);
652
653     /* Remember existing pad functions */
654     priv->eventfunc = GST_PAD_EVENTFUNC (internal);
655     priv->queryfunc = GST_PAD_QUERYFUNC (internal);
656     gst_pad_set_element_private (internal, priv);
657
658     g_object_weak_ref ((GObject *) internal,
659         (GWeakNotify) internal_pad_finalizing, priv);
660
661     /* add query/event function overrides on internal pad */
662     gst_pad_set_event_function (internal,
663         GST_DEBUG_FUNCPTR (internalpad_event_function));
664     gst_pad_set_query_function (internal,
665         GST_DEBUG_FUNCPTR (internalpad_query_function));
666   }
667
668   priv->object = object;
669   priv->ghostpriv = privghost;
670   priv->dir = GST_PAD_DIRECTION (ghostpad);
671   gst_object_unref (internal);
672
673   GST_DEBUG_OBJECT (ghostpad, "Done with pad %s:%s",
674       GST_DEBUG_PAD_NAME (ghostpad));
675 }
676
677
678 /**
679  * gnl_object_ghost_pad:
680  * @object: #GnlObject to add the ghostpad to
681  * @name: Name for the new pad
682  * @target: Target #GstPad to ghost
683  *
684  * Adds a #GstGhostPad overridding the correct pad [query|event]_function so
685  * that time shifting is done correctly
686  * The #GstGhostPad is added to the #GnlObject
687  *
688  * /!\ This function doesn't check if the existing [src|sink] pad was removed
689  * first, so you might end up with more pads than wanted
690  *
691  * Returns: The #GstPad if everything went correctly, else NULL.
692  */
693 GstPad *
694 gnl_object_ghost_pad (GnlObject * object, const gchar * name, GstPad * target)
695 {
696   GstPadDirection dir = GST_PAD_DIRECTION (target);
697   GstPad *ghost;
698
699   GST_DEBUG_OBJECT (object, "name:%s, target:%p", name, target);
700
701   g_return_val_if_fail (target, FALSE);
702   g_return_val_if_fail ((dir != GST_PAD_UNKNOWN), FALSE);
703
704   ghost = gnl_object_ghost_pad_no_target (object, name, dir, NULL);
705   if (!ghost) {
706     GST_WARNING_OBJECT (object, "Couldn't create ghostpad");
707     return NULL;
708   }
709
710   if (!(gnl_object_ghost_pad_set_target (object, ghost, target))) {
711     GST_WARNING_OBJECT (object,
712         "Couldn't set the target pad... removing ghostpad");
713     gst_object_unref (ghost);
714     return NULL;
715   }
716
717   GST_DEBUG_OBJECT (object, "activating ghostpad");
718   /* activate pad */
719   gst_pad_set_active (ghost, TRUE);
720   /* add it to element */
721   if (!(gst_element_add_pad (GST_ELEMENT (object), ghost))) {
722     GST_WARNING ("couldn't add newly created ghostpad");
723     return NULL;
724   }
725
726   return ghost;
727 }
728
729 /*
730  * gnl_object_ghost_pad_no_target:
731  * /!\ Doesn't add the pad to the GnlObject....
732  */
733 GstPad *
734 gnl_object_ghost_pad_no_target (GnlObject * object, const gchar * name,
735     GstPadDirection dir, GstPadTemplate * template)
736 {
737   GstPad *ghost;
738   GnlPadPrivate *priv;
739
740   /* create a no_target ghostpad */
741   if (template)
742     ghost = gst_ghost_pad_new_no_target_from_template (name, template);
743   else
744     ghost = gst_ghost_pad_new_no_target (name, dir);
745   if (!ghost)
746     return NULL;
747
748
749   /* remember the existing ghostpad event/query/link/unlink functions */
750   priv = g_slice_new0 (GnlPadPrivate);
751   priv->dir = dir;
752   priv->object = object;
753
754   /* grab/replace event/query functions */
755   GST_DEBUG_OBJECT (ghost, "Setting priv->eventfunc to %p",
756       GST_PAD_EVENTFUNC (ghost));
757   priv->eventfunc = GST_PAD_EVENTFUNC (ghost);
758   priv->queryfunc = GST_PAD_QUERYFUNC (ghost);
759
760   gst_pad_set_event_function (ghost,
761       GST_DEBUG_FUNCPTR (ghostpad_event_function));
762   gst_pad_set_query_function (ghost,
763       GST_DEBUG_FUNCPTR (ghostpad_query_function));
764
765   gst_pad_set_element_private (ghost, priv);
766   control_internal_pad (ghost, object);
767
768   return ghost;
769 }
770
771
772
773 void
774 gnl_object_remove_ghost_pad (GnlObject * object, GstPad * ghost)
775 {
776   GnlPadPrivate *priv;
777
778   GST_DEBUG_OBJECT (object, "ghostpad %s:%s", GST_DEBUG_PAD_NAME (ghost));
779
780   priv = gst_pad_get_element_private (ghost);
781   gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), NULL);
782   gst_element_remove_pad (GST_ELEMENT (object), ghost);
783   if (priv)
784     g_slice_free (GnlPadPrivate, priv);
785 }
786
787 gboolean
788 gnl_object_ghost_pad_set_target (GnlObject * object, GstPad * ghost,
789     GstPad * target)
790 {
791   GnlPadPrivate *priv = gst_pad_get_element_private (ghost);
792
793   g_return_val_if_fail (priv, FALSE);
794   g_return_val_if_fail (GST_IS_PAD (ghost), FALSE);
795
796   if (target) {
797     GST_DEBUG_OBJECT (object, "setting target %s:%s on %s:%s",
798         GST_DEBUG_PAD_NAME (target), GST_DEBUG_PAD_NAME (ghost));
799   } else {
800     GST_ERROR_OBJECT (object, "removing target from ghostpad");
801     priv->pending_seek = NULL;
802   }
803
804   /* set target */
805   if (!(gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), target))) {
806     GST_WARNING_OBJECT (priv->object, "Could not set ghost %s:%s "
807         "target to: %s:%s", GST_DEBUG_PAD_NAME (ghost),
808         GST_DEBUG_PAD_NAME (target));
809     return FALSE;
810   }
811
812   if (target && priv->pending_seek) {
813     gboolean res = gst_pad_send_event (ghost, priv->pending_seek);
814
815     GST_INFO_OBJECT (object, "Sending our pending seek event: %" GST_PTR_FORMAT
816         " -- Result is %i", priv->pending_seek, res);
817
818     priv->pending_seek = NULL;
819   }
820
821   return TRUE;
822 }
823
824 void
825 gnl_init_ghostpad_category (void)
826 {
827   GST_DEBUG_CATEGORY_INIT (gnlghostpad, "gnlghostpad",
828       GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin GhostPad");
829
830 }