e43677774aeefdfcf01d3fe96afeb0cf38c47a70
[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 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include "gstspideridentity.h"
28 #include "gstspider.h"
29
30 GST_DEBUG_CATEGORY_STATIC (gst_spider_identity_debug);
31 #define GST_CAT_DEFAULT gst_spider_identity_debug
32
33 static GstElementDetails gst_spider_identity_details = GST_ELEMENT_DETAILS (
34   "SpiderIdentity",
35   "Generic",
36   "Link between spider and outside elements",
37   "Benjamin Otte <in7y118@public.uni-hamburg.de>"
38 );
39
40
41 /* generic templates 
42  * delete me when meging with spider.c
43  */
44 static GstStaticPadTemplate spider_src_factory =
45 GST_STATIC_PAD_TEMPLATE (
46   "src",
47   GST_PAD_SRC,
48   GST_PAD_ALWAYS,
49   GST_STATIC_CAPS_ANY
50 );
51
52 static GstStaticPadTemplate spider_sink_factory =
53 GST_STATIC_PAD_TEMPLATE (
54   "sink",
55   GST_PAD_SINK,
56   GST_PAD_ALWAYS,
57   GST_STATIC_CAPS_ANY
58 );
59
60 /* SpiderIdentity signals and args */
61 enum {
62   /* FILL ME */
63   LAST_SIGNAL
64 };
65
66 enum {
67   ARG_0,
68   /* FILL ME */
69 };
70
71 /* GObject stuff */
72 static void                     gst_spider_identity_class_init          (GstSpiderIdentityClass *klass);
73 static void                     gst_spider_identity_init                (GstSpiderIdentity *spider_identity);
74
75 /* functions set in pads, elements and stuff */
76 static void                     gst_spider_identity_chain               (GstPad *pad, GstBuffer *buf);
77 static GstElementStateReturn    gst_spider_identity_change_state        (GstElement *element);
78 static GstPadLinkReturn         gst_spider_identity_link                (GstPad *pad, const GstCaps *caps);
79 static GstCaps *                gst_spider_identity_getcaps             (GstPad *pad);
80 /* loop functions */
81 static void                     gst_spider_identity_dumb_loop           (GstSpiderIdentity *ident);
82 static void                     gst_spider_identity_src_loop            (GstSpiderIdentity *ident);
83 static void                     gst_spider_identity_sink_loop_type_finding (GstSpiderIdentity *ident);
84
85 static gboolean                 gst_spider_identity_handle_src_event    (GstPad *pad, GstEvent *event);
86
87 /* other functions */
88 static void                     gst_spider_identity_start_type_finding  (GstSpiderIdentity *ident);
89
90 static GstElementClass *        parent_class                            = NULL;
91 /* no signals
92 static guint gst_spider_identity_signals[LAST_SIGNAL] = { 0 }; */
93
94 GType
95 gst_spider_identity_get_type (void) 
96 {
97   static GType spider_identity_type = 0;
98
99   if (!spider_identity_type) {
100     static const GTypeInfo spider_identity_info = {
101       sizeof(GstSpiderIdentityClass),      NULL,
102       NULL,
103       (GClassInitFunc)gst_spider_identity_class_init,
104       NULL,
105       NULL,
106       sizeof(GstSpiderIdentity),
107       0,
108       (GInstanceInitFunc)gst_spider_identity_init,
109     };
110     spider_identity_type = g_type_register_static (GST_TYPE_ELEMENT, "GstSpiderIdentity", 
111                                                    &spider_identity_info, 0);
112     GST_DEBUG_CATEGORY_INIT (gst_spider_identity_debug, "spideridentity", 
113                              0, "spider autoplugging proxy element");
114   }
115   return spider_identity_type;
116 }
117
118 static void 
119 gst_spider_identity_class_init (GstSpiderIdentityClass *klass) 
120 {
121   GstElementClass *gstelement_class = (GstElementClass *) klass;
122   
123   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
124   
125   /* add our two pad templates */
126   gst_element_class_add_pad_template (gstelement_class,
127       gst_static_pad_template_get (&spider_src_factory));
128   gst_element_class_add_pad_template (gstelement_class,
129       gst_static_pad_template_get (&spider_sink_factory));
130   gst_element_class_set_details (gstelement_class, &gst_spider_identity_details);
131   
132   gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_spider_identity_change_state);
133   gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_spider_identity_request_new_pad);
134 }
135
136 static void 
137 gst_spider_identity_init (GstSpiderIdentity *ident) 
138 {
139   /* sink */
140   ident->sink = gst_pad_new_from_template (
141       gst_static_pad_template_get (&spider_sink_factory), "sink");
142   gst_element_add_pad (GST_ELEMENT (ident), ident->sink);
143   gst_pad_set_link_function (ident->sink, GST_DEBUG_FUNCPTR (gst_spider_identity_link));
144   gst_pad_set_getcaps_function (ident->sink, GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
145   /* src */
146   ident->src = gst_pad_new_from_template (
147       gst_static_pad_template_get (&spider_src_factory), "src");
148   gst_element_add_pad (GST_ELEMENT (ident), ident->src);
149   gst_pad_set_link_function (ident->src, GST_DEBUG_FUNCPTR (gst_spider_identity_link));
150   gst_pad_set_getcaps_function (ident->src, GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
151   gst_pad_set_event_function (ident->src, GST_DEBUG_FUNCPTR (gst_spider_identity_handle_src_event));
152
153   /* variables */
154   ident->plugged = FALSE;
155 }
156
157 static void 
158 gst_spider_identity_chain (GstPad *pad, GstBuffer *buf) 
159 {
160   GstSpiderIdentity *ident;
161   
162   /*g_print ("chaining on pad %s:%s with buffer %p\n", GST_DEBUG_PAD_NAME (pad), buf);*/
163
164   g_return_if_fail (pad != NULL);
165   g_return_if_fail (GST_IS_PAD (pad));
166   if (buf == NULL) return;
167
168   ident = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
169
170   if (GST_IS_EVENT (buf)) {
171     /* start hack for current event stuff here */
172     /* check for unlinked elements and send them the EOS event, too */
173     if (GST_EVENT_TYPE (GST_EVENT (buf)) == GST_EVENT_EOS)
174     {
175       GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (ident);
176       GList *list = spider->links;
177       while (list)
178       {
179         GstSpiderConnection *conn = (GstSpiderConnection *) list->data;
180         list = g_list_next (list);
181         if (conn->current != (GstElement *) conn->src) {
182           GST_DEBUG ("sending EOS to unconnected element %s from %s", 
183                GST_ELEMENT_NAME (conn->src), GST_ELEMENT_NAME (ident));
184           gst_pad_push (conn->src->src, GST_DATA (GST_BUFFER (gst_event_new (GST_EVENT_EOS))));  
185           gst_element_set_eos (GST_ELEMENT (conn->src));
186         }
187       }
188     }
189     /* end hack for current event stuff here */
190
191     gst_pad_event_default (pad, GST_EVENT (buf));
192     return;
193   }
194
195   if ((ident->src != NULL) && (GST_PAD_PEER (ident->src) != NULL)) {
196     /* 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)); */
197     GST_LOG ( "push %p %" G_GINT64_FORMAT, buf, GST_BUFFER_OFFSET (buf));
198     gst_pad_push (ident->src, GST_DATA (buf));
199   } else if (GST_IS_BUFFER (buf)) {
200     gst_buffer_unref (buf);
201   }
202 }
203 GstSpiderIdentity*           
204 gst_spider_identity_new_src (gchar *name)
205 {
206   GstSpiderIdentity *ret = (GstSpiderIdentity *) gst_element_factory_make ("spideridentity", name);
207   /* set the right functions */
208   gst_element_set_loop_function (GST_ELEMENT (ret), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_src_loop));
209   
210   return ret;
211 }
212 GstSpiderIdentity*           
213 gst_spider_identity_new_sink (gchar *name)
214 {
215   GstSpiderIdentity *ret = (GstSpiderIdentity *) gst_element_factory_make ("spideridentity", name);
216
217   /* set the right functions */
218   gst_element_set_loop_function (GST_ELEMENT (ret), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
219
220   return ret;
221 }
222
223 /* shamelessly stolen from gstqueue.c to get proxy links */
224 static GstPadLinkReturn
225 gst_spider_identity_link (GstPad *pad, const GstCaps *caps)
226 {
227   GstSpiderIdentity *spider_identity = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
228   GstPad *otherpad;
229
230   if (pad == spider_identity->src) 
231     otherpad = spider_identity->sink;
232   else
233     otherpad = spider_identity->src;
234
235   g_return_val_if_fail (otherpad != NULL, GST_PAD_LINK_REFUSED);
236   if (GST_PAD_PEER (otherpad) == NULL)
237     return GST_PAD_LINK_DELAYED;
238
239   return gst_pad_try_set_caps (otherpad, gst_caps_copy (caps));
240 }
241
242 static GstCaps*
243 gst_spider_identity_getcaps (GstPad *pad)
244 {
245   GstSpiderIdentity *ident = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
246   GstPad *otherpad;
247
248   if (pad == ident->src) 
249     otherpad = ident->sink;
250   else
251     otherpad = ident->src;
252
253   if (otherpad != NULL) {
254     GstPad *peer = GST_PAD_PEER (otherpad);
255
256     if (peer) {
257       GstCaps *ret = gst_pad_get_caps (peer);
258       if (ident->caps) {
259         GstCaps *ret2 = gst_caps_intersect (ident->caps, ret);
260         gst_caps_free (ret);
261         ret = ret2;
262       }
263       return ret;
264     }
265   }
266   if (ident->caps)
267     return gst_caps_copy (ident->caps);
268
269   return gst_caps_new_any ();
270 }
271
272 GstPad*
273 gst_spider_identity_request_new_pad  (GstElement *element, GstPadTemplate *templ, const gchar *name)
274 {
275   GstSpiderIdentity *ident;
276   
277   /*checks */
278   g_return_val_if_fail (templ != NULL, NULL);
279   g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
280   ident = GST_SPIDER_IDENTITY (element);
281   g_return_val_if_fail (ident != NULL, NULL);
282   g_return_val_if_fail (GST_IS_SPIDER_IDENTITY (ident), NULL);
283   
284   switch (GST_PAD_TEMPLATE_DIRECTION (templ))
285   {
286     case GST_PAD_SINK:
287       if (ident->sink != NULL) break;
288       /* sink */
289       GST_DEBUG ( "element %s requests new sink pad", GST_ELEMENT_NAME(ident));
290       ident->sink = gst_pad_new ("sink", GST_PAD_SINK);
291       gst_element_add_pad (GST_ELEMENT (ident), ident->sink);
292       gst_pad_set_link_function (ident->sink, GST_DEBUG_FUNCPTR (gst_spider_identity_link));
293       gst_pad_set_getcaps_function (ident->sink, GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
294       return ident->sink;
295     case GST_PAD_SRC:
296       /* src */
297       if (ident->src != NULL) break;
298       GST_DEBUG ( "element %s requests new src pad", GST_ELEMENT_NAME(ident));
299       ident->src = gst_pad_new ("src", GST_PAD_SRC);
300       gst_element_add_pad (GST_ELEMENT (ident), ident->src);
301       gst_pad_set_link_function (ident->src, GST_DEBUG_FUNCPTR (gst_spider_identity_link));
302       gst_pad_set_getcaps_function (ident->src, GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
303       gst_pad_set_event_function (ident->src, GST_DEBUG_FUNCPTR (gst_spider_identity_handle_src_event));
304       return ident->src;
305     default:
306       break;
307   }
308   
309   GST_DEBUG ( "element %s requested a new pad but none could be created", GST_ELEMENT_NAME(ident));
310   return NULL;
311 }
312
313 /* this function has to
314  * - start the autoplugger
315  * - start type finding
316  * ...
317  */
318 static GstElementStateReturn
319 gst_spider_identity_change_state (GstElement *element)
320 {
321   GstSpiderIdentity *ident;
322   GstSpider *spider;
323   GstElementStateReturn ret = GST_STATE_SUCCESS;
324   
325   /* element check */
326   ident = GST_SPIDER_IDENTITY (element);
327   g_return_val_if_fail (ident != NULL, GST_STATE_FAILURE);
328   g_return_val_if_fail (GST_IS_SPIDER_IDENTITY (ident), GST_STATE_FAILURE);
329   
330   switch (GST_STATE_TRANSITION (element)) {
331     case GST_STATE_PAUSED_TO_READY:
332       gst_caps_replace (&ident->caps, NULL);
333       break;
334     case GST_STATE_PAUSED_TO_PLAYING:
335       /* autoplugger check */
336       spider = GST_SPIDER (GST_ELEMENT_PARENT (ident));
337       g_return_val_if_fail (spider != NULL, GST_STATE_FAILURE);
338       g_return_val_if_fail (GST_IS_SPIDER (spider), GST_STATE_FAILURE);
339   
340       /* start typefinding or plugging */
341       if ((GST_RPAD_PEER (ident->sink) != NULL) && (GST_RPAD_PEER (ident->src) == NULL))
342       {
343         GstCaps *caps = gst_pad_get_caps ((GstPad *) GST_PAD_PEER (ident->sink));
344         if (gst_caps_is_any (caps) || gst_caps_is_empty (caps))
345         {
346           gst_spider_identity_start_type_finding (ident);
347           gst_caps_free (caps);
348           break;
349         } else {
350           gst_spider_identity_plug (ident);
351         }
352         gst_caps_free (caps);
353       }
354       /* autoplug on src */
355       if ((GST_RPAD_PEER (ident->src) != NULL) && (GST_RPAD_PEER (ident->sink) == NULL))
356       {
357         gst_spider_identity_plug (ident);
358       }
359     default:
360       break;
361   }
362   
363   if ((ret != GST_STATE_FAILURE) && (GST_ELEMENT_CLASS (parent_class)->change_state))
364     ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
365   
366   return ret;
367 }
368
369 static void
370 gst_spider_identity_start_type_finding (GstSpiderIdentity *ident)
371 {
372 /*  GstElement* typefind;
373   gchar *name;*/
374   gboolean restart = FALSE;
375   
376   GST_DEBUG ("element %s starts typefinding", GST_ELEMENT_NAME(ident));
377   if (GST_STATE (GST_ELEMENT_PARENT (ident)) == GST_STATE_PLAYING)
378   {
379     gst_element_set_state (GST_ELEMENT (GST_ELEMENT_PARENT (ident)), GST_STATE_PAUSED);
380     restart = TRUE;
381   }
382
383   gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_sink_loop_type_finding));
384
385   if (restart)
386   {
387     gst_element_set_state (GST_ELEMENT (GST_ELEMENT_PARENT (ident)), GST_STATE_PLAYING);
388   }
389 }
390
391 /* since we can't set the loop function to NULL if there's a cothread for us,
392  * we have to use a dumb one
393  */
394 static void
395 gst_spider_identity_dumb_loop  (GstSpiderIdentity *ident)
396 {
397   GstBuffer *buf;
398
399   g_return_if_fail (ident != NULL);
400   g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
401   g_assert (ident->sink != NULL);
402
403   buf = GST_BUFFER (gst_pad_pull (ident->sink));
404
405   gst_spider_identity_chain (ident->sink, buf);
406 }
407 /* do nothing until we're linked - then disable yourself
408  */
409 static void
410 gst_spider_identity_src_loop (GstSpiderIdentity *ident)
411 {
412   /* checks - disable for speed */
413   g_return_if_fail (ident != NULL);
414   g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
415   
416   /* we don't want a loop function if we're plugged */
417   if (ident->sink && GST_PAD_PEER (ident->sink))
418   {
419     gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
420     gst_spider_identity_dumb_loop (ident);
421     return;
422   }
423   gst_element_interrupt (GST_ELEMENT (ident));
424 }
425 /* This loop function is only needed when typefinding.
426  */
427 typedef struct {
428   GstBuffer *buffer;
429   guint best_probability;
430   GstCaps *caps;
431 } SpiderTypeFind;
432 guint8 *
433 spider_find_peek (gpointer data, gint64 offset, guint size)
434 {
435   SpiderTypeFind *find = (SpiderTypeFind *) data;
436   gint64 buffer_offset = GST_BUFFER_OFFSET_IS_VALID (find->buffer) ? 
437                          GST_BUFFER_OFFSET (find->buffer) : 0;
438   
439   if (offset >= buffer_offset && offset + size <= buffer_offset + GST_BUFFER_SIZE (find->buffer)) {
440     GST_LOG ("peek %"G_GINT64_FORMAT", %u successful", offset, size);
441     return GST_BUFFER_DATA (find->buffer) + offset - buffer_offset;
442   } else {
443     GST_LOG ("peek %"G_GINT64_FORMAT", %u failed", offset, size);
444     return NULL;
445   }
446 }
447 static void
448 spider_find_suggest (gpointer data, guint probability, const GstCaps *caps)
449 {
450   SpiderTypeFind *find = (SpiderTypeFind *) data;
451   G_GNUC_UNUSED gchar *caps_str;
452
453   caps_str = gst_caps_to_string (caps);
454   GST_INFO ("suggest %u, %s", probability, caps_str);
455   g_free (caps_str);
456   if (probability > find->best_probability) {
457     gst_caps_replace (&find->caps, gst_caps_copy (caps));
458     find->best_probability = probability;
459   }
460 }
461 static void
462 gst_spider_identity_sink_loop_type_finding (GstSpiderIdentity *ident)
463 {
464   GstData *data;
465   GstTypeFind gst_find;
466   SpiderTypeFind find;
467   GList *walk, *type_list = NULL;
468
469   g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
470
471   data = gst_pad_pull (ident->sink);
472   while (!GST_IS_BUFFER (data)) {
473     gst_spider_identity_chain (ident->sink, GST_BUFFER (data));
474     data = gst_pad_pull (ident->sink);
475   }
476   
477   find.buffer = GST_BUFFER (data);
478   /* maybe there are already valid caps now? */
479   find.caps = gst_pad_get_caps (ident->sink);
480   if (! gst_caps_is_empty (find.caps) && !gst_caps_is_any (find.caps)) {
481     goto plug;
482   } else {
483     gst_caps_free (find.caps);
484     find.caps = NULL;
485   }
486   
487   /* now do the actual typefinding with the supplied buffer */
488   walk = type_list = gst_type_find_factory_get_list ();
489     
490   find.best_probability = 0;
491   find.caps = NULL;
492   gst_find.data = &find;
493   gst_find.peek = spider_find_peek;
494   gst_find.suggest = spider_find_suggest;
495   while (walk) {
496     GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (walk->data);
497
498     GST_DEBUG ("trying typefind function %s", GST_PLUGIN_FEATURE_NAME (factory));
499     gst_type_find_factory_call_function (factory, &gst_find);
500     if (find.best_probability >= GST_TYPE_FIND_MAXIMUM)
501       goto plug;
502     walk = g_list_next (walk);
503   }
504   if (find.best_probability > 0)
505     goto plug;
506   gst_element_error(GST_ELEMENT(ident), "Could not find media type", NULL);
507   find.buffer = GST_BUFFER (gst_event_new (GST_EVENT_EOS));
508
509 end:
510   /* remove loop function */
511   gst_element_set_loop_function (GST_ELEMENT (ident), 
512                                 (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
513   
514   /* push the buffer */
515   gst_spider_identity_chain (ident->sink, find.buffer);
516   
517   return;
518
519 plug:
520   GST_INFO ("typefind function found caps"); 
521   ident->caps = find.caps;
522   g_assert (GST_PAD_LINK_SUCCESSFUL (gst_pad_try_set_caps (ident->src, find.caps)));
523   {
524     gchar *str = gst_caps_to_string (find.caps);
525     GST_LOG_OBJECT (ident, "spider starting caps: %s", str);
526     g_free (str);
527   }
528   if (type_list)
529     g_list_free (type_list);
530
531   gst_spider_identity_plug (ident);
532
533   goto end;
534 }
535
536 static gboolean
537 gst_spider_identity_handle_src_event (GstPad *pad, GstEvent *event)
538 {
539   gboolean res = TRUE;
540   GstSpiderIdentity *ident;
541
542   GST_DEBUG ( "spider_identity src_event");
543
544   ident = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
545
546   switch (GST_EVENT_TYPE (event)) {
547     case GST_EVENT_FLUSH:
548     case GST_EVENT_SEEK:
549     default:
550       res = gst_pad_event_default (pad, event);
551       break;
552   }
553
554   return res;
555 }