fix segfault when pushing events twice
[platform/upstream/gstreamer.git] / gst / autoplug / gstspideridentity.c
1 /* GStreamer
2  * Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
3  *               2002 Wim Taymans <wtay@chello.be>
4  *
5  * gstspideridentity.c: IDentity element for the spider autoplugger
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23
24 #include "gstspideridentity.h"
25
26 #include "gstspider.h"
27
28 GstElementDetails gst_spider_identity_details = {
29   "SpiderIdentity",
30   "Filter/Autoplug",
31   "connection between spider and outside elements",
32   VERSION,
33   "Benjamin Otte <in7y118@public.uni-hamburg.de>",
34   "(C) 2002",
35 };
36
37
38 /* generic templates 
39  * delete me when meging with spider.c
40  */
41 GST_PADTEMPLATE_FACTORY (spider_src_factory,
42   "src_%02d",
43   GST_PAD_SRC,
44   GST_PAD_REQUEST,
45   NULL      /* no caps */
46 );
47
48 GST_PADTEMPLATE_FACTORY (spider_sink_factory,
49   "sink_%02d",
50   GST_PAD_SINK,
51   GST_PAD_REQUEST,
52   NULL      /* no caps */
53 );
54
55 /* SpiderIdentity signals and args */
56 enum {
57   /* FILL ME */
58   LAST_SIGNAL
59 };
60
61 enum {
62   ARG_0,
63   /* FILL ME */
64 };
65
66 /* GObject stuff */
67 static void                   gst_spider_identity_class_init            (GstSpiderIdentityClass *klass);
68 static void                   gst_spider_identity_init                  (GstSpiderIdentity *spider_identity);
69
70 /* functions set in pads, elements and stuff */
71 static void                   gst_spider_identity_chain                  (GstPad *pad, GstBuffer *buf);
72 static GstElementStateReturn  gst_spider_identity_change_state          (GstElement *element);
73 static GstPadConnectReturn    gst_spider_identity_connect                (GstPad *pad, GstCaps *caps);
74 static GstCaps*                gst_spider_identity_getcaps                (GstPad *pad, GstCaps *caps);
75 /* loop functions */
76 static void                    gst_spider_identity_dumb_loop              (GstSpiderIdentity *ident);
77 static void                    gst_spider_identity_src_loop              (GstSpiderIdentity *ident);
78 static void                    gst_spider_identity_sink_loop_typefinding  (GstSpiderIdentity *ident);
79 static void                    gst_spider_identity_sink_loop_emptycache  (GstSpiderIdentity *ident);
80
81 /* set/get functions */
82 gboolean                      gst_spider_identity_is_plugged            (GstSpiderIdentity *identity);
83 void                          gst_spider_identity_set_caps              (GstSpiderIdentity *identity, GstCaps *caps);
84
85 /* callback */
86 static void                    callback_typefind_have_type                (GstElement *typefind, GstCaps *caps, GstSpiderIdentity *identity);
87
88 /* other functions */
89 static void                    gst_spider_identity_start_typefinding      (GstSpiderIdentity *ident);
90
91 static                        GstElementClass                           *parent_class = NULL;
92 /* no signals
93 static guint gst_spider_identity_signals[LAST_SIGNAL] = { 0 }; */
94
95 GType
96 gst_spider_identity_get_type (void) 
97 {
98   static GType spider_identity_type = 0;
99
100   if (!spider_identity_type) {
101     static const GTypeInfo spider_identity_info = {
102       sizeof(GstSpiderIdentityClass),      NULL,
103       NULL,
104       (GClassInitFunc)gst_spider_identity_class_init,
105       NULL,
106       NULL,
107       sizeof(GstSpiderIdentity),
108       0,
109       (GInstanceInitFunc)gst_spider_identity_init,
110     };
111     spider_identity_type = g_type_register_static (GST_TYPE_ELEMENT, "GstSpiderIdentity", &spider_identity_info, 0);
112   }
113   return spider_identity_type;
114 }
115
116 static void 
117 gst_spider_identity_class_init (GstSpiderIdentityClass *klass) 
118 {
119   GstElementClass *gstelement_class = (GstElementClass *) klass;
120   
121   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
122   
123   /* add our two pad templates */
124   gst_element_class_add_padtemplate (gstelement_class, GST_PADTEMPLATE_GET (spider_src_factory));
125   gst_element_class_add_padtemplate (gstelement_class, GST_PADTEMPLATE_GET (spider_sink_factory));
126   
127   gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_spider_identity_change_state);
128   gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_spider_identity_request_new_pad);
129 }
130
131 static GstBufferPool*
132 gst_spider_identity_get_bufferpool (GstPad *pad)
133 {
134   /* fix me */
135   GstSpiderIdentity *spider_identity;
136
137   spider_identity = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
138
139   return gst_pad_get_bufferpool (spider_identity->src);
140 }
141
142 static void 
143 gst_spider_identity_init (GstSpiderIdentity *spider_identity) 
144 {
145   /* pads */
146   spider_identity->sink = NULL;
147   spider_identity->src = NULL;
148
149   /* variables */
150   spider_identity->plugged = FALSE;
151   
152   /* caching */
153   spider_identity->cache_start = NULL;
154   spider_identity->cache_end = NULL;
155   
156 }
157
158 static void 
159 gst_spider_identity_chain (GstPad *pad, GstBuffer *buf) 
160 {
161   GstSpiderIdentity *ident;
162   
163   /* g_print ("chaining on pad %s:%s with buffer %p\n", GST_DEBUG_PAD_NAME (pad), buf); */
164
165   g_return_if_fail (pad != NULL);
166   g_return_if_fail (GST_IS_PAD (pad));
167   if (buf == NULL) return;
168
169   ident = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
170
171   if (GST_IS_EVENT (buf)) {
172     gst_pad_event_default (ident->sink, GST_EVENT (buf));
173     return;
174   }
175
176   if ((ident->src != NULL) && (GST_PAD_PEER (ident->src) != NULL)) {
177     /* g_print("pushing buffer %p (refcount %d - buffersize %d) to pad %s:%s\n", buf, GST_BUFFER_REFCOUNT (buf), GST_BUFFER_SIZE (buf), GST_DEBUG_PAD_NAME (ident->src)); */
178     gst_pad_push (ident->src, buf);
179   } else if (GST_IS_BUFFER (buf)) {
180     gst_buffer_unref (buf);
181   }
182 }
183 GstSpiderIdentity*           
184 gst_spider_identity_new_src (gchar *name)
185 {
186   GstSpiderIdentity *ret = (GstSpiderIdentity *) g_object_new (gst_spider_identity_get_type (), NULL);
187   
188   GST_ELEMENT_NAME (ret) = name;
189   /* set the right functions */
190   gst_element_set_loop_function (GST_ELEMENT (ret), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_src_loop));
191   
192   return ret;
193 }
194 GstSpiderIdentity*           
195 gst_spider_identity_new_sink (gchar *name)
196 {
197   GstSpiderIdentity *ret = (GstSpiderIdentity *) g_object_new (gst_spider_identity_get_type (), NULL);
198   
199   GST_ELEMENT_NAME (ret) = name;
200
201   return ret;
202 }
203
204 /* shamelessly stolen from gstqueue.c to get proxy connections */
205 static GstPadConnectReturn
206 gst_spider_identity_connect (GstPad *pad, GstCaps *caps)
207 {
208   GstSpiderIdentity *spider_identity = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
209   GstPad *otherpad;
210
211   if (pad == spider_identity->src) 
212     otherpad = spider_identity->sink;
213   else
214     otherpad = spider_identity->src;
215
216   if (otherpad != NULL)
217     return gst_pad_proxy_connect (otherpad, caps);
218   
219   return GST_PAD_CONNECT_OK;
220 }
221
222 static GstCaps*
223 gst_spider_identity_getcaps (GstPad *pad, GstCaps *caps)
224 {
225   GstSpiderIdentity *spider_identity = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
226   GstPad *otherpad;
227
228   if (pad == spider_identity->src) 
229     otherpad = spider_identity->sink;
230   else
231     otherpad = spider_identity->src;
232
233   if (otherpad != NULL)
234     return gst_pad_get_allowed_caps (otherpad);
235   return NULL;
236 }
237
238 GstPad*
239 gst_spider_identity_request_new_pad  (GstElement *element, GstPadTemplate *templ, const gchar *name)
240 {
241   GstSpiderIdentity *ident;
242   
243   /*checks */
244   g_return_val_if_fail (templ != NULL, NULL);
245   g_return_val_if_fail (GST_IS_PADTEMPLATE (templ), NULL);
246   ident = GST_SPIDER_IDENTITY (element);
247   g_return_val_if_fail (ident != NULL, NULL);
248   g_return_val_if_fail (GST_IS_SPIDER_IDENTITY (ident), NULL);
249   
250   switch (GST_PADTEMPLATE_DIRECTION (templ))
251   {
252     case GST_PAD_SINK:
253       if (ident->sink != NULL) break;
254       /* sink */
255       GST_DEBUG(0, "element %s requests new sink pad\n", GST_ELEMENT_NAME(ident));
256       ident->sink = gst_pad_new ("sink", GST_PAD_SINK);
257       gst_element_add_pad (GST_ELEMENT (ident), ident->sink);
258       gst_pad_set_connect_function (ident->sink, GST_DEBUG_FUNCPTR (gst_spider_identity_connect));
259       gst_pad_set_getcaps_function (ident->sink, GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
260       return ident->sink;
261     case GST_PAD_SRC:
262       /* src */
263       if (ident->src != NULL) break;
264       GST_DEBUG(0, "element %s requests new src pad\n", GST_ELEMENT_NAME(ident));
265       ident->src = gst_pad_new ("src", GST_PAD_SRC);
266       gst_element_add_pad (GST_ELEMENT (ident), ident->src);
267       gst_pad_set_connect_function (ident->src, GST_DEBUG_FUNCPTR (gst_spider_identity_connect));
268       gst_pad_set_getcaps_function (ident->src, GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
269       return ident->src;
270     default:
271       break;
272   }
273   
274   GST_DEBUG(0, "element %s requested a new pad but none could be created\n", GST_ELEMENT_NAME(ident));
275   return NULL;
276 }
277
278 /* this function has to
279  * - start the autoplugger
280  * - start type finding
281  * ...
282  */
283 static GstElementStateReturn
284 gst_spider_identity_change_state (GstElement *element)
285 {
286   GstSpiderIdentity *ident;
287   GstSpider *spider;
288   GstElementStateReturn ret = GST_STATE_SUCCESS;
289   
290   /* element check */
291   ident = GST_SPIDER_IDENTITY (element);
292   g_return_val_if_fail (ident != NULL, GST_PAD_CONNECT_REFUSED);
293   g_return_val_if_fail (GST_IS_SPIDER_IDENTITY (ident), GST_PAD_CONNECT_REFUSED);
294   
295   /* autoplugger check */
296   spider = GST_SPIDER (GST_ELEMENT_PARENT (ident));
297   g_return_val_if_fail (spider != NULL, GST_PAD_CONNECT_REFUSED);
298   g_return_val_if_fail (GST_IS_SPIDER (spider), GST_PAD_CONNECT_REFUSED);
299   
300   switch (GST_STATE_TRANSITION (element)) {
301     case GST_STATE_PAUSED_TO_PLAYING:
302       /* start typefinding or plugging */
303       if ((ident->sink != NULL) && (ident->src == NULL))
304       {
305         if (gst_pad_get_caps ((GstPad *) GST_PAD_PEER (ident->sink)) == NULL)
306         {
307           gst_spider_identity_start_typefinding (ident);
308           break;
309         } else {
310           gst_spider_plug (ident);
311         }
312       }
313       /* autoplug on src */
314       if ((ident->src != NULL) && (ident->sink == NULL))
315       {
316         gst_spider_plug (ident);
317       }
318     default:
319       break;
320   }
321   
322   if ((ret != GST_STATE_FAILURE) && (GST_ELEMENT_CLASS (parent_class)->change_state))
323     ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
324   
325   return ret;
326 }
327
328 static void
329 gst_spider_identity_start_typefinding (GstSpiderIdentity *ident)
330 {
331   GstElement* typefind;
332   
333   GST_DEBUG (GST_CAT_AUTOPLUG, "element %s starts typefinding", GST_ELEMENT_NAME(ident));
334   
335   /* create and connect typefind object */
336   typefind = gst_elementfactory_make ("typefind", g_strdup_printf("%s%s", "typefind", GST_ELEMENT_NAME(ident)));
337   g_signal_connect (G_OBJECT (typefind), "have_type",
338                     G_CALLBACK (callback_typefind_have_type), ident);
339   gst_bin_add (GST_BIN (GST_ELEMENT_PARENT (ident)), typefind);
340   gst_pad_connect (gst_element_get_compatible_pad ((GstElement *) ident, gst_element_get_pad (typefind, "sink")), gst_element_get_pad (typefind, "sink"));
341   
342   gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_sink_loop_typefinding));
343 }
344 /* waiting for a good suggestion on where to set the caps from typefinding
345  * Caps must be cleared when pad is disconnected
346  * 
347  * Currently we are naive and set the caps on the source of the identity object 
348  * directly and hope to avoid any disturbance in the force.
349  */
350 void
351 gst_spider_identity_set_caps (GstSpiderIdentity *ident, GstCaps *caps)
352 {
353   if (ident->src)
354   {
355     gst_pad_try_set_caps (ident->src, caps);
356   }
357 }
358
359
360 static void
361 callback_typefind_have_type (GstElement *typefind, GstCaps *caps, GstSpiderIdentity *ident)
362 {
363   gboolean restart_spider = FALSE;
364   
365   GST_INFO (GST_CAT_AUTOPLUG, "element %s has found caps", GST_ELEMENT_NAME(ident));
366   /* checks */
367   
368   /* we have to ref the typefind, because if me remove it the scheduler segfaults 
369    * FIXME: get rid of the typefinder
370    */
371   gst_object_ref (GST_OBJECT (typefind));
372   
373   g_assert (GST_IS_ELEMENT (typefind));
374   g_assert (GST_IS_SPIDER_IDENTITY (ident));
375   
376   /* pause the autoplugger */
377   if (gst_element_get_state (GST_ELEMENT (GST_ELEMENT_PARENT(ident))) == GST_STATE_PLAYING)
378   {
379     gst_element_set_state (GST_ELEMENT (GST_ELEMENT_PARENT(ident)), GST_STATE_PAUSED);
380     restart_spider = TRUE;
381   }
382
383   /* remove typefind */
384   gst_pad_disconnect (ident->src, (GstPad*) GST_RPAD_PEER (ident->src));
385   gst_bin_remove (GST_BIN (GST_ELEMENT_PARENT (ident)), typefind);
386   
387   /* set caps */
388   gst_spider_identity_set_caps (ident, caps);
389   
390   /* set new loop function, we gotta empty the cache */
391   gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_sink_loop_emptycache));
392   
393   /* autoplug this pad */
394   gst_spider_plug (ident);  
395   
396   /* restart autoplugger */
397   if (restart_spider)
398     gst_element_set_state (GST_ELEMENT (GST_ELEMENT_PARENT(ident)), GST_STATE_PLAYING);
399   
400 }
401 /* since we can't set the loop function to NULL if there's a cothread for us,
402  * we have to use a dumb one
403  */
404 static void
405 gst_spider_identity_dumb_loop  (GstSpiderIdentity *ident)
406 {
407   GstBuffer *buf;
408
409   g_return_if_fail (ident != NULL);
410   g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
411   g_assert (ident->sink != NULL);
412
413   buf = gst_pad_pull (ident->sink);
414
415   gst_spider_identity_chain (ident->sink, buf);
416 }
417 /* do nothing until we're connected - then disable yourself
418  */
419 static void
420 gst_spider_identity_src_loop (GstSpiderIdentity *ident)
421 {
422   /* checks - disable for speed */
423   g_return_if_fail (ident != NULL);
424   g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
425   
426   /* we don't want a loop function if we're plugged */
427   if (ident->sink && GST_PAD_PEER (ident->sink))
428   {
429     gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
430     gst_spider_identity_dumb_loop (ident);
431     return;
432   }
433   
434   /* in any case, we don't want to do anything:
435    * - if we're not plugged, we don't have buffers
436    * - if we're plugged, we wanna be chained please
437    */
438   gst_element_interrupt (GST_ELEMENT (ident));
439   return;  
440 }
441 /* This loop function is only needed when typefinding.
442  * It works quite simple: get a new buffer, append it to the cache
443  * and push it to the typefinder.
444  */
445 static void
446 gst_spider_identity_sink_loop_typefinding (GstSpiderIdentity *ident)
447 {
448   GstBuffer *buf;
449   
450   /* checks - disable for speed */
451   g_return_if_fail (ident != NULL);
452   g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
453   g_assert (ident->sink != NULL);
454   
455   /* get buffer */
456   buf = gst_pad_pull (ident->sink);
457   
458   /* if it's an event... */
459   if (GST_IS_EVENT (buf)) {
460     /* handle DISCONT events, please */
461     gst_pad_event_default (ident->sink, GST_EVENT (buf));
462   } 
463
464   /* add it to the end of the cache */
465   gst_buffer_ref (buf);
466   GST_DEBUG (0, "element %s adds buffer %p (size %d) to cache\n", GST_ELEMENT_NAME(ident),  buf, GST_BUFFER_SIZE (buf));
467   ident->cache_end = g_list_prepend (ident->cache_end, buf);
468   if (ident->cache_start == NULL)
469     ident->cache_start = ident->cache_end;
470   
471   /* push the buffer */
472   gst_spider_identity_chain (ident->sink, buf);
473 }
474 /* this function is needed after typefinding:
475  * empty the cache and when the cache is empty - remove this function
476  */
477 static void
478 gst_spider_identity_sink_loop_emptycache (GstSpiderIdentity *ident)
479 {
480   GstBuffer *buf;
481   
482   /* get the buffer and push it */
483   buf = GST_BUFFER (ident->cache_start->data);
484   gst_spider_identity_chain (ident->sink, buf);
485   
486   ident->cache_start = g_list_previous (ident->cache_start);
487
488   /* now check if we have more buffers to push */
489   if (ident->cache_start == NULL)
490   {
491     GST_DEBUG(0, "cache from %s is empty, changing loop function\n", GST_ELEMENT_NAME(ident));
492     /* free cache */
493     g_list_free (ident->cache_end);
494     ident->cache_end = NULL;
495     
496     /* remove loop function */
497     gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
498     gst_element_interrupt (GST_ELEMENT (ident));
499   }  
500 }
501
502
503