2 * Copyright (C) 2001 RidgeRun, Inc. (www.ridgerun.com)
4 * gstautoplugcache.c: Data cache for the dynamic autoplugger
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.
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.
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.
24 GstElementDetails gst_autoplugcache_details = {
27 "Data cache for the dynamic autoplugger",
29 "Erik Walthinsen <omega@temple-baptist.com>",
30 "(C) 2001 RidgeRun, Inc. (www.ridgerun.com)",
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))
44 typedef struct _GstAutoplugCache GstAutoplugCache;
45 typedef struct _GstAutoplugCacheClass GstAutoplugCacheClass;
47 struct _GstAutoplugCache {
50 GstPad *sinkpad, *srcpad;
57 GList *current_playout;
62 struct _GstAutoplugCacheClass {
63 GstElementClass parent_class;
65 void (*first_buffer) (GstElement *element, GstBuffer *buf);
66 void (*cache_empty) (GstElement *element);
70 /* Cache signals and args */
85 static void gst_autoplugcache_class_init (GstAutoplugCacheClass *klass);
86 static void gst_autoplugcache_init (GstAutoplugCache *cache);
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);
93 static void gst_autoplugcache_loop (GstElement *element);
95 static GstElementStateReturn gst_autoplugcache_change_state (GstElement *element);
98 static GstElementClass *parent_class = NULL;
99 static guint gst_autoplugcache_signals[LAST_SIGNAL] = { 0 };
102 gst_autoplugcache_get_type(void) {
103 static GType autoplugcache_type = 0;
105 if (!autoplugcache_type) {
106 static const GTypeInfo autoplugcache_info = {
107 sizeof(GstAutoplugCacheClass),
110 (GClassInitFunc)gst_autoplugcache_class_init,
113 sizeof(GstAutoplugCache),
115 (GInstanceInitFunc)gst_autoplugcache_init,
117 autoplugcache_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAutoplugCache", &autoplugcache_info, 0);
119 return autoplugcache_type;
123 gst_autoplugcache_class_init (GstAutoplugCacheClass *klass)
125 GObjectClass *gobject_class;
126 GstElementClass *gstelement_class;
128 gobject_class = (GObjectClass*)klass;
129 gstelement_class = (GstElementClass*)klass;
131 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
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! */
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,
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);
153 gobject_class->set_property = gst_autoplugcache_set_property;
154 gobject_class->get_property = gst_autoplugcache_get_property;
156 gstelement_class->change_state = gst_autoplugcache_change_state;
160 gst_autoplugcache_init (GstAutoplugCache *cache)
162 gst_element_set_loop_function(GST_ELEMENT(cache), GST_DEBUG_FUNCPTR(gst_autoplugcache_loop));
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);
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);
172 cache->caps_proxy = FALSE;
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;
184 gst_autoplugcache_loop (GstElement *element)
186 GstAutoplugCache *cache;
187 GstBuffer *buf = NULL;
189 cache = GST_AUTOPLUGCACHE (element);
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.
204 /* the first time through, the current_playout pointer is going to be NULL */
205 if (cache->current_playout == NULL) {
207 buf = gst_pad_pull (cache->sinkpad);
208 if (GST_IS_EVENT (buf)) {
209 gst_pad_event_default (cache->sinkpad, GST_EVENT (buf));
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++;
218 /* set the current_playout pointer */
219 cache->current_playout = cache->cache;
221 g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[FIRST_BUFFER], 0, buf);
223 /* send the buffer on its way */
224 gst_pad_push (cache->srcpad, buf);
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) {
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));
241 gst_object_unref (GST_OBJECT (cache));
245 buf = gst_pad_pull (cache->sinkpad);
246 if (GST_IS_EVENT (buf)) {
247 gst_pad_event_default (cache->sinkpad, GST_EVENT (buf));
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++;
256 /* set the current_playout pointer */
257 cache->current_playout = cache->cache;
259 /* send the buffer on its way */
260 gst_pad_push (cache->srcpad, buf);
263 /* otherwise we're trundling through existing cached buffers */
265 /* move the current_playout pointer */
266 cache->current_playout = g_list_previous (cache->current_playout);
268 if (cache->fire_first) {
269 g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[FIRST_BUFFER], 0, buf);
270 cache->fire_first = FALSE;
273 /* push that buffer */
274 gst_pad_push (cache->srcpad, GST_BUFFER(cache->current_playout->data));
278 static GstElementStateReturn
279 gst_autoplugcache_change_state (GstElement *element)
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);
285 return GST_STATE_SUCCESS;
289 gst_autoplugcache_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
291 GstAutoplugCache *cache;
293 cache = GST_AUTOPLUGCACHE (object);
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) {
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;
321 gst_autoplugcache_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
323 GstAutoplugCache *cache;
325 cache = GST_AUTOPLUGCACHE (object);
328 case ARG_BUFFER_COUNT:
329 g_value_set_int (value, cache->buffer_count);
332 g_value_set_boolean (value, cache->caps_proxy);
335 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
341 plugin_init (GModule *module, GstPlugin *plugin)
343 GstElementFactory *factory;
345 factory = gst_element_factory_new ("autoplugcache", GST_TYPE_AUTOPLUGCACHE,
346 &gst_autoplugcache_details);
347 g_return_val_if_fail (factory != NULL, FALSE);
349 gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
354 GstPluginDesc plugin_desc = {