- Removed unused locking from the cothreads
[platform/upstream/gstreamer.git] / gst / autoplug / gstautoplugcache.c
1 /* GStreamer
2  * Copyright (C) 2001 RidgeRun, Inc. (www.ridgerun.com)
3  *
4  * gstautoplugcache.c: Data cache for the dynamic autoplugger
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <gst/gst.h>
23
24 GstElementDetails gst_autoplugcache_details = {
25   "AutoplugCache",
26   "Generic",
27   "Data cache for the dynamic autoplugger",
28   VERSION,
29   "Erik Walthinsen <omega@temple-baptist.com>",
30   "(C) 2001 RidgeRun, Inc. (www.ridgerun.com)",
31 };
32
33 #define GST_TYPE_AUTOPLUGCACHE \
34   (gst_autoplugcache_get_type())
35 #define GST_AUTOPLUGCACHE(obj) \
36   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUTOPLUGCACHE,GstAutoplugCache))
37 #define GST_AUTOPLUGCACHE_CLASS(klass) \
38   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUTOPLUGCACHE,GstAutoplugCacheClass))
39 #define GST_IS_AUTOPLUGCACHE(obj) \
40   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUTOPLUGCACHE))
41 #define GST_IS_AUTOPLUGCACHE_CLASS(obj) \
42   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUTOPLUGCACHE))
43
44 typedef struct _GstAutoplugCache GstAutoplugCache;
45 typedef struct _GstAutoplugCacheClass GstAutoplugCacheClass;
46
47 struct _GstAutoplugCache {
48   GstElement element;
49
50   GstPad *sinkpad, *srcpad;
51
52   gboolean caps_proxy;
53
54   GList *cache;
55   GList *cache_start;
56   gint buffer_count;
57   GList *current_playout;
58   gboolean fire_empty;
59   gboolean fire_first;
60 };
61
62 struct _GstAutoplugCacheClass {
63   GstElementClass parent_class;
64
65   void          (*first_buffer)         (GstElement *element, GstBuffer *buf);
66   void          (*cache_empty)          (GstElement *element);
67 };
68
69
70 /* Cache signals and args */
71 enum {
72   FIRST_BUFFER,
73   CACHE_EMPTY,
74   LAST_SIGNAL
75 };
76
77 enum {
78   ARG_0,
79   ARG_BUFFER_COUNT,
80   ARG_CAPS_PROXY,
81   ARG_RESET
82 };
83
84
85 static void                     gst_autoplugcache_class_init    (GstAutoplugCacheClass *klass);
86 static void                     gst_autoplugcache_init          (GstAutoplugCache *cache);
87
88 static void                     gst_autoplugcache_set_property  (GObject *object, guint prop_id, 
89                                                                  const GValue *value, GParamSpec *pspec);
90 static void                     gst_autoplugcache_get_property  (GObject *object, guint prop_id, 
91                                                                  GValue *value, GParamSpec *pspec);
92
93 static void                     gst_autoplugcache_loop          (GstElement *element);
94
95 static GstElementStateReturn    gst_autoplugcache_change_state  (GstElement *element);
96
97
98 static GstElementClass *parent_class = NULL;
99 static guint gst_autoplugcache_signals[LAST_SIGNAL] = { 0 };
100
101 GType
102 gst_autoplugcache_get_type(void) {
103   static GType autoplugcache_type = 0;
104
105   if (!autoplugcache_type) {
106     static const GTypeInfo autoplugcache_info = {
107       sizeof(GstAutoplugCacheClass),
108       NULL,
109       NULL,
110       (GClassInitFunc)gst_autoplugcache_class_init,
111       NULL,
112       NULL,
113       sizeof(GstAutoplugCache),
114       0,
115       (GInstanceInitFunc)gst_autoplugcache_init,
116     };
117     autoplugcache_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAutoplugCache", &autoplugcache_info, 0);
118   }
119   return autoplugcache_type;
120 }
121
122 static void
123 gst_autoplugcache_class_init (GstAutoplugCacheClass *klass)
124 {
125   GObjectClass *gobject_class;
126   GstElementClass *gstelement_class;
127
128   gobject_class = (GObjectClass*)klass;
129   gstelement_class = (GstElementClass*)klass;
130
131   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
132
133   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFFER_COUNT,
134     g_param_spec_int("buffer_count","buffer_count","buffer_count",
135                      0,G_MAXINT,0,G_PARAM_READABLE)); /* CHECKME! */
136   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CAPS_PROXY,
137     g_param_spec_boolean("caps_proxy","caps_proxy","caps_proxy",
138                          FALSE,G_PARAM_READWRITE)); /* CHECKME! */
139   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_RESET,
140     g_param_spec_boolean("reset","reset","reset",
141                          FALSE,G_PARAM_WRITABLE)); /* CHECKME! */
142
143   gst_autoplugcache_signals[FIRST_BUFFER] =
144     g_signal_new ("first_buffer", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
145                     G_STRUCT_OFFSET (GstAutoplugCacheClass, first_buffer), NULL, NULL,
146                     g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
147                     G_TYPE_POINTER);
148   gst_autoplugcache_signals[CACHE_EMPTY] =
149     g_signal_new ("cache_empty", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
150                     G_STRUCT_OFFSET (GstAutoplugCacheClass, cache_empty), NULL, NULL,
151                     g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
152
153   gobject_class->set_property = gst_autoplugcache_set_property;
154   gobject_class->get_property = gst_autoplugcache_get_property;
155
156   gstelement_class->change_state = gst_autoplugcache_change_state;
157 }
158
159 static void
160 gst_autoplugcache_init (GstAutoplugCache *cache)
161 {
162   gst_element_set_loop_function(GST_ELEMENT(cache), GST_DEBUG_FUNCPTR(gst_autoplugcache_loop));
163
164   cache->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
165 /*  gst_pad_set_negotiate_function (cache->sinkpad, gst_autoplugcache_nego_sink); */
166   gst_element_add_pad (GST_ELEMENT(cache), cache->sinkpad);
167
168   cache->srcpad = gst_pad_new ("src", GST_PAD_SRC);
169 /*  gst_pad_set_negotiate_function (cache->srcpad, gst_autoplugcache_nego_src); */
170   gst_element_add_pad (GST_ELEMENT(cache), cache->srcpad);
171
172   cache->caps_proxy = FALSE;
173
174   /* provide a zero basis for the cache */
175   cache->cache = g_list_prepend(NULL, NULL);
176   cache->cache_start = cache->cache;
177   cache->buffer_count = 0;
178   cache->current_playout = 0;
179   cache->fire_empty = FALSE;
180   cache->fire_first = FALSE;
181 }
182
183 static void
184 gst_autoplugcache_loop (GstElement *element)
185 {
186   GstAutoplugCache *cache;
187   GstBuffer *buf = NULL;
188
189   cache = GST_AUTOPLUGCACHE (element);
190
191   /* Theory:
192    * The cache is a doubly-linked list.  The front of the list is the most recent
193    * buffer, the end of the list is the first buffer.  The playout pointer always
194    * points to the latest buffer sent out the end.  cache points to the front
195    * (most reccent) of the list at all times.  cache_start points to the first 
196    * buffer, i.e. the end of the list.
197    * If the playout pointer does not have a prev (towards the most recent) buffer 
198    * (== NULL), a buffer must be pulled from the sink pad and added to the cache.
199    * When the playout pointer gets reset (as in a set_property), the cache is walked
200    * without problems, because the playout pointer has a non-NULL next.  When
201    * the playout pointer hits the end of cache again it has to start pulling.
202    */
203
204   /* the first time through, the current_playout pointer is going to be NULL */
205   if (cache->current_playout == NULL) {
206     /* get a buffer */
207     buf = gst_pad_pull (cache->sinkpad);
208     if (GST_IS_EVENT (buf)) {
209       gst_pad_event_default (cache->sinkpad, GST_EVENT (buf));
210       return;
211     }
212
213     /* add it to the cache, though cache == NULL */
214     gst_buffer_ref (buf);
215     cache->cache = g_list_prepend (cache->cache, buf);
216     cache->buffer_count++;
217
218     /* set the current_playout pointer */
219     cache->current_playout = cache->cache;
220
221     g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[FIRST_BUFFER], 0, buf);
222
223     /* send the buffer on its way */
224     gst_pad_push (cache->srcpad, buf);
225   }
226   /* the steady state is where the playout is at the front of the cache */
227   else if (g_list_previous(cache->current_playout) == NULL) {
228
229     /* if we've been told to fire an empty signal (after a reset) */
230     if (cache->fire_empty) {
231       int oldstate = GST_STATE(cache);
232       GST_DEBUG(0,"at front of cache, about to pull, but firing signal");
233       gst_object_ref (GST_OBJECT (cache));
234       g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[CACHE_EMPTY], 0, NULL);
235       if (GST_STATE(cache) != oldstate) {
236         gst_object_ref (GST_OBJECT (cache));
237         GST_DEBUG(GST_CAT_AUTOPLUG, "state changed during signal, aborting");
238         gst_element_yield (GST_ELEMENT (cache));
239         return;
240       }
241       gst_object_unref (GST_OBJECT (cache));
242     }
243
244     /* get a buffer */
245     buf = gst_pad_pull (cache->sinkpad);
246     if (GST_IS_EVENT (buf)) {
247       gst_pad_event_default (cache->sinkpad, GST_EVENT (buf));
248       return;
249     }
250
251     /* add it to the front of the cache */
252     gst_buffer_ref (buf);
253     cache->cache = g_list_prepend (cache->cache, buf);
254     cache->buffer_count++;
255
256     /* set the current_playout pointer */
257     cache->current_playout = cache->cache;
258
259     /* send the buffer on its way */
260     gst_pad_push (cache->srcpad, buf);
261   }
262
263   /* otherwise we're trundling through existing cached buffers */
264   else {
265     /* move the current_playout pointer */
266     cache->current_playout = g_list_previous (cache->current_playout);
267
268     if (cache->fire_first) {
269       g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[FIRST_BUFFER], 0, buf);
270       cache->fire_first = FALSE;
271     }
272
273     /* push that buffer */
274     gst_pad_push (cache->srcpad, GST_BUFFER(cache->current_playout->data));
275   }
276 }
277
278 static GstElementStateReturn
279 gst_autoplugcache_change_state (GstElement *element)
280 {
281   /* FIXME this should do something like free the cache on ->NULL */
282   if (GST_ELEMENT_CLASS (parent_class)->change_state)
283     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
284
285   return GST_STATE_SUCCESS;
286 }
287
288 static void
289 gst_autoplugcache_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
290 {
291   GstAutoplugCache *cache;
292
293   cache = GST_AUTOPLUGCACHE (object);
294
295   switch (prop_id) {
296     case ARG_CAPS_PROXY:
297       cache->caps_proxy = g_value_get_boolean (value);
298 GST_DEBUG(0,"caps_proxy is %d",cache->caps_proxy);
299       if (cache->caps_proxy) {
300       } else {
301       }
302       break;
303     case ARG_RESET:
304       /* no idea why anyone would set this to FALSE, but just in case ;-) */
305       if (g_value_get_boolean (value)) {
306         GST_DEBUG(0,"resetting playout pointer");
307         /* reset the playout pointer to the begining again */
308         cache->current_playout = cache->cache_start;
309         /* now we can fire a signal when the cache runs dry */
310         cache->fire_empty = TRUE;
311         /* also set it up to fire the first_buffer signal again */
312         cache->fire_first = TRUE;
313       }
314       break;
315     default:
316       break;
317   }
318 }
319
320 static void
321 gst_autoplugcache_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
322 {
323   GstAutoplugCache *cache;
324
325   cache = GST_AUTOPLUGCACHE (object);
326
327   switch (prop_id) {
328     case ARG_BUFFER_COUNT:
329       g_value_set_int (value, cache->buffer_count);
330       break;
331     case ARG_CAPS_PROXY:
332       g_value_set_boolean (value, cache->caps_proxy);
333       break;
334     default:
335       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
336       break;
337   }
338 }
339
340 static gboolean
341 plugin_init (GModule *module, GstPlugin *plugin)
342 {
343   GstElementFactory *factory;
344
345   factory = gst_element_factory_new ("autoplugcache", GST_TYPE_AUTOPLUGCACHE,
346                                     &gst_autoplugcache_details);
347   g_return_val_if_fail (factory != NULL, FALSE);
348
349   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
350
351   return TRUE;
352 }
353
354 GstPluginDesc plugin_desc = {
355   GST_VERSION_MAJOR,
356   GST_VERSION_MINOR,
357   "autoplugcache",
358   plugin_init
359 };
360