upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / ext / gconf / gstswitchsrc.c
1 /* GStreamer
2  * Copyright (c) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
3  * Copyright (c) 2006 Jürg Billeter <j@bitron.ch>
4  * Copyright (c) 2007 Jan Schmidt <thaytan@noraisin.net>
5  * Copyright (c) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
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 <string.h>
28
29 #include "gstswitchsrc.h"
30
31 GST_DEBUG_CATEGORY_STATIC (switch_debug);
32 #define GST_CAT_DEFAULT switch_debug
33
34 static void gst_switch_src_dispose (GObject * object);
35 static GstStateChangeReturn
36 gst_switch_src_change_state (GstElement * element, GstStateChange transition);
37
38 GST_BOILERPLATE (GstSwitchSrc, gst_switch_src, GstBin, GST_TYPE_BIN);
39
40 static void
41 gst_switch_src_base_init (gpointer klass)
42 {
43   GST_DEBUG_CATEGORY_INIT (switch_debug, "switchsrc", 0, "switchsrc element");
44 }
45
46 static void
47 gst_switch_src_class_init (GstSwitchSrcClass * klass)
48 {
49   GObjectClass *oklass = G_OBJECT_CLASS (klass);
50   GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
51   static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
52       GST_PAD_SRC,
53       GST_PAD_ALWAYS,
54       GST_STATIC_CAPS_ANY);
55   GstPadTemplate *child_pad_templ;
56
57   oklass->dispose = gst_switch_src_dispose;
58   eklass->change_state = gst_switch_src_change_state;
59
60   /* Provide a default pad template if the child didn't */
61   child_pad_templ = gst_element_class_get_pad_template (eklass, "src");
62   if (child_pad_templ == NULL) {
63     gst_element_class_add_pad_template (eklass,
64         gst_static_pad_template_get (&src_template));
65   }
66 }
67
68 static gboolean
69 gst_switch_src_reset (GstSwitchSrc * src)
70 {
71   /* this will install fakesrc if no other child has been set,
72    * otherwise we rely on the subclass to know when to unset its
73    * custom kid */
74   if (src->kid == NULL) {
75     return gst_switch_src_set_child (src, NULL);
76   }
77
78   return TRUE;
79 }
80
81 static void
82 gst_switch_src_init (GstSwitchSrc * src, GstSwitchSrcClass * g_class)
83 {
84   GstElementClass *eklass = GST_ELEMENT_GET_CLASS (src);
85   GstPadTemplate *templ;
86
87   templ = gst_element_class_get_pad_template (eklass, "src");
88   src->pad = gst_ghost_pad_new_no_target_from_template ("src", templ);
89   gst_element_add_pad (GST_ELEMENT (src), src->pad);
90
91   gst_switch_src_reset (src);
92
93   GST_OBJECT_FLAG_SET (src, GST_ELEMENT_IS_SOURCE);
94 }
95
96 static void
97 gst_switch_src_dispose (GObject * object)
98 {
99   GstSwitchSrc *src = GST_SWITCH_SRC (object);
100   GstObject *new_kid, *kid;
101
102   GST_OBJECT_LOCK (src);
103   new_kid = GST_OBJECT_CAST (src->new_kid);
104   src->new_kid = NULL;
105
106   kid = GST_OBJECT_CAST (src->kid);
107   src->kid = NULL;
108   GST_OBJECT_UNLOCK (src);
109
110   gst_object_replace (&new_kid, NULL);
111   gst_object_replace (&kid, NULL);
112
113   GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
114 }
115
116 static gboolean
117 gst_switch_src_commit_new_kid (GstSwitchSrc * src)
118 {
119   GstPad *targetpad;
120   GstState kid_state;
121   GstElement *new_kid, *old_kid;
122   gboolean is_fakesrc = FALSE;
123   GstBus *bus;
124
125   /* need locking around member accesses */
126   GST_OBJECT_LOCK (src);
127   /* If we're currently changing state, set the child to the next state
128    * we're transitioning too, rather than our current state which is 
129    * about to change */
130   if (GST_STATE_NEXT (src) != GST_STATE_VOID_PENDING)
131     kid_state = GST_STATE_NEXT (src);
132   else
133     kid_state = GST_STATE (src);
134
135   new_kid = src->new_kid ? gst_object_ref (src->new_kid) : NULL;
136   src->new_kid = NULL;
137   GST_OBJECT_UNLOCK (src);
138
139   /* Fakesrc by default if NULL is passed as the new child */
140   if (new_kid == NULL) {
141     GST_DEBUG_OBJECT (src, "Replacing kid with fakesrc");
142     new_kid = gst_element_factory_make ("fakesrc", "testsrc");
143     if (new_kid == NULL) {
144       GST_ERROR_OBJECT (src, "Failed to create fakesrc");
145       return FALSE;
146     }
147     /* Add a reference, as it would if the element came from src->new_kid */
148     gst_object_ref (new_kid);
149     is_fakesrc = TRUE;
150   } else {
151     GST_DEBUG_OBJECT (src, "Setting new kid");
152   }
153
154   /* set temporary bus of our own to catch error messages from the child
155    * (could we just set our own bus on it, or would the state change messages
156    * from the not-yet-added element confuse the state change algorithm? Let's
157    * play it safe for now) */
158   bus = gst_bus_new ();
159   gst_element_set_bus (new_kid, bus);
160   gst_object_unref (bus);
161
162   if (gst_element_set_state (new_kid, kid_state) == GST_STATE_CHANGE_FAILURE) {
163     GstMessage *msg;
164
165     /* check if child posted an error message and if so re-post it on our bus
166      * so that the application gets to see a decent error and not our generic
167      * fallback error message which is completely indecipherable to the user */
168     msg = gst_bus_pop_filtered (GST_ELEMENT_BUS (new_kid), GST_MESSAGE_ERROR);
169     if (msg) {
170       GST_INFO_OBJECT (src, "Forwarding kid error: %" GST_PTR_FORMAT, msg);
171       gst_element_post_message (GST_ELEMENT (src), msg);
172     }
173     GST_ELEMENT_ERROR (src, CORE, STATE_CHANGE, (NULL),
174         ("Failed to set state on new child."));
175     gst_element_set_bus (new_kid, NULL);
176     gst_object_unref (new_kid);
177     return FALSE;
178   }
179   gst_element_set_bus (new_kid, NULL);
180   gst_bin_add (GST_BIN (src), new_kid);
181
182   /* Now, replace the existing child */
183   GST_OBJECT_LOCK (src);
184   old_kid = src->kid;
185   src->kid = new_kid;
186   /* Mark whether a custom kid or fakesrc has been installed */
187   src->have_kid = !is_fakesrc;
188   GST_OBJECT_UNLOCK (src);
189
190   /* kill old element */
191   if (old_kid) {
192     GST_DEBUG_OBJECT (src, "Removing old kid %" GST_PTR_FORMAT, old_kid);
193     gst_element_set_state (old_kid, GST_STATE_NULL);
194     gst_bin_remove (GST_BIN (src), old_kid);
195     gst_object_unref (old_kid);
196     /* Don't lose the SOURCE flag */
197     GST_OBJECT_FLAG_SET (src, GST_ELEMENT_IS_SOURCE);
198   }
199
200   /* re-attach ghostpad */
201   GST_DEBUG_OBJECT (src, "Creating new ghostpad");
202   targetpad = gst_element_get_static_pad (src->kid, "src");
203   gst_ghost_pad_set_target (GST_GHOST_PAD (src->pad), targetpad);
204   gst_object_unref (targetpad);
205   GST_DEBUG_OBJECT (src, "done changing child of switchsrc");
206
207   return TRUE;
208 }
209
210 gboolean
211 gst_switch_src_set_child (GstSwitchSrc * src, GstElement * new_kid)
212 {
213   GstState cur, next;
214   GstElement **p_kid;
215
216   /* Nothing to do if clearing the child and we've already installed fakesrc */
217   if (new_kid == NULL && src->kid != NULL && src->have_kid == FALSE)
218     return TRUE;
219
220   /* Store the new kid to be committed later */
221   GST_OBJECT_LOCK (src);
222   cur = GST_STATE (src);
223   next = GST_STATE_NEXT (src);
224   p_kid = &src->new_kid;
225   gst_object_replace ((GstObject **) p_kid, (GstObject *) new_kid);
226   GST_OBJECT_UNLOCK (src);
227   if (new_kid)
228     gst_object_unref (new_kid);
229
230   /* Sometime, it would be lovely to allow src changes even when
231    * already running */
232   /* FIXME: Block the pad and replace the kid when it completes */
233   if (cur > GST_STATE_READY || next == GST_STATE_PAUSED) {
234     GST_DEBUG_OBJECT (src,
235         "Switch-src is already running. Ignoring change of child.");
236     gst_object_unref (new_kid);
237     return TRUE;
238   }
239
240   return gst_switch_src_commit_new_kid (src);
241 }
242
243 static GstStateChangeReturn
244 gst_switch_src_change_state (GstElement * element, GstStateChange transition)
245 {
246   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
247   GstSwitchSrc *src = GST_SWITCH_SRC (element);
248
249   ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state,
250       (element, transition), GST_STATE_CHANGE_SUCCESS);
251
252   switch (transition) {
253     case GST_STATE_CHANGE_READY_TO_NULL:
254       if (!gst_switch_src_reset (src))
255         ret = GST_STATE_CHANGE_FAILURE;
256       break;
257     default:
258       break;
259   }
260
261   return ret;
262 }