update plugin initialization restructuring (see email for details
[platform/upstream/gstreamer.git] / gst / elements / gstbufferstore.c
1 /* GStreamer
2  * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
3  *
4  * gstbufferstore.c: keep an easily accessible list of all buffers
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 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25 #include "gstbufferstore.h"
26 #include <string.h>
27
28 GST_DEBUG_CATEGORY_STATIC (gst_buffer_store_debug);
29 #define GST_CAT_DEFAULT gst_buffer_store_debug
30
31 enum {
32   CLEARED,
33   BUFFER_ADDED,
34   LAST_SIGNAL
35 };
36 enum {
37   ARG_0
38 };
39
40
41 static void     gst_buffer_store_class_init     (gpointer               g_class,
42                                                  gpointer               class_data);
43 static void     gst_buffer_store_init           (GTypeInstance *        instance,
44                                                  gpointer               g_class);
45 static void     gst_buffer_store_dispose        (GObject *              object);
46
47 static gboolean gst_buffer_store_add_buffer_func (GstBufferStore *      store, 
48                                                  GstBuffer *            buffer);
49 static void     gst_buffer_store_cleared_func   (GstBufferStore *       store);
50   
51 static GObjectClass *parent_class = NULL;
52 static guint gst_buffer_store_signals[LAST_SIGNAL] = { 0 };
53
54 G_GNUC_UNUSED static void
55 debug_buffers (GstBufferStore *store)
56 {
57   GList *walk = store->buffers;
58   
59   g_printerr ("BUFFERS in store:\n");
60   while (walk) {
61     g_print ("%15"G_GUINT64_FORMAT" - %7u\n", GST_BUFFER_OFFSET (walk->data), GST_BUFFER_SIZE (walk->data));
62     walk = g_list_next (walk);
63   }
64   g_printerr ("\n");
65 }
66 GType
67 gst_buffer_store_get_type (void)
68 {
69   static GType store_type = 0;
70
71   if (!store_type) {
72     static const GTypeInfo store_info = {
73       sizeof (GstBufferStoreClass),
74       NULL,
75       NULL,
76       gst_buffer_store_class_init,
77       NULL,
78       NULL,
79       sizeof (GstBufferStore),
80       0,
81       gst_buffer_store_init,
82       NULL
83     };
84     store_type = g_type_register_static (G_TYPE_OBJECT,
85                                             "GstBufferStore",
86                                             &store_info, 0);
87   
88     /* FIXME: better description anyone? */
89     GST_DEBUG_CATEGORY_INIT (gst_buffer_store_debug, "bufferstore", 0, "store all data");
90   }
91   return store_type;
92 }
93 static gboolean
94 continue_accu (GSignalInvocationHint *ihint, GValue *return_accu, 
95                const GValue *handler_return, gpointer data)
96 {
97   gboolean do_continue = g_value_get_boolean (handler_return);
98   g_value_set_boolean (return_accu, do_continue);
99
100   return do_continue;
101 }
102 static void
103 gst_buffer_store_class_init (gpointer g_class, gpointer class_data)
104 {
105   GObjectClass *gobject_class;
106   GstBufferStoreClass *store_class;
107
108   gobject_class = G_OBJECT_CLASS (g_class);
109   store_class = GST_BUFFER_STORE_CLASS (g_class);
110
111   parent_class = g_type_class_peek_parent (g_class);
112
113   gobject_class->dispose = gst_buffer_store_dispose;
114   
115   gst_buffer_store_signals[CLEARED] = g_signal_new ("cleared", 
116           G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
117           G_STRUCT_OFFSET (GstBufferStoreClass, cleared), NULL, NULL,
118           gst_marshal_VOID__VOID, G_TYPE_NONE, 0);
119   gst_buffer_store_signals[BUFFER_ADDED] = g_signal_new ("buffer-added", 
120           G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
121           G_STRUCT_OFFSET (GstBufferStoreClass, buffer_added), continue_accu, NULL,
122           gst_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
123
124   store_class->cleared = gst_buffer_store_cleared_func;
125   store_class->buffer_added = gst_buffer_store_add_buffer_func;
126 }
127 static void
128 gst_buffer_store_init (GTypeInstance *instance, gpointer g_class)
129 {
130   GstBufferStore *store = GST_BUFFER_STORE (instance);
131   
132   store->buffers = NULL;
133 }
134 static void
135 gst_buffer_store_dispose (GObject *object)
136 {
137   GstBufferStore *store = GST_BUFFER_STORE (object);
138
139   gst_buffer_store_clear (store);
140
141   parent_class->dispose (object);
142 }
143 static gboolean
144 gst_buffer_store_add_buffer_func (GstBufferStore *store, GstBuffer *buffer)
145 {
146   g_assert (buffer != NULL);
147   
148   if (!GST_BUFFER_OFFSET_IS_VALID (buffer) &&
149       store->buffers &&
150       GST_BUFFER_OFFSET_IS_VALID (store->buffers->data)) {
151     /* we assumed valid offsets, but suddenly they are not anymore */
152     GST_DEBUG_OBJECT (store, "attempting to add buffer %p with invalid offset to store with valid offset, abort",
153             buffer);
154     return FALSE;
155   } else if (!store->buffers || !GST_BUFFER_OFFSET_IS_VALID (store->buffers->data)) {
156     /* the starting buffer had an invalid offset, in that case we assume continuous buffers */
157     GST_LOG_OBJECT (store, "adding buffer %p with invalid offset and size %u",
158             buffer, GST_BUFFER_SIZE (buffer));
159     gst_data_ref (GST_DATA (buffer));
160     store->buffers = g_list_append (store->buffers, buffer);
161     return TRUE;
162   } else {
163     /* both list and buffer have valid offsets, we can really go wild */
164     GList *walk, *current_list = NULL;
165     GstBuffer *current;
166     
167     g_assert (GST_BUFFER_OFFSET_IS_VALID (buffer));
168     GST_LOG_OBJECT (store, "attempting to add buffer %p with offset %"G_GUINT64_FORMAT" and size %u",
169             buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
170     /* we keep a sorted list of non-overlapping buffers */
171     walk = store->buffers;
172     while (walk) {
173       current = GST_BUFFER (walk->data);
174       current_list = walk;
175       walk = g_list_next (walk);
176       if (GST_BUFFER_OFFSET (current) < GST_BUFFER_OFFSET (buffer)) {
177         continue;
178       } else if (GST_BUFFER_OFFSET (current) == GST_BUFFER_OFFSET (buffer)) {
179         guint needed_size;
180         if (walk) {
181           needed_size = MIN (GST_BUFFER_SIZE (buffer), 
182                   GST_BUFFER_OFFSET (walk->data) - GST_BUFFER_OFFSET (current));
183         } else {
184           needed_size = GST_BUFFER_SIZE (buffer);
185         }
186         if (needed_size <= GST_BUFFER_SIZE (current)) {
187           buffer = NULL;
188           break;
189         } else {
190           if (needed_size < GST_BUFFER_SIZE (buffer)) {
191             /* need to create subbuffer to not have overlapping data */
192             GstBuffer *sub = gst_buffer_create_sub (buffer, 0, needed_size);
193             g_assert (sub);
194             buffer = sub;
195           } else {
196             gst_data_ref (GST_DATA (buffer));
197           }
198           /* replace current buffer with new one */
199           GST_INFO_OBJECT (store, "replacing buffer %p with buffer %p with offset %"G_GINT64_FORMAT" and size %u", 
200                            current_list->data, buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
201           gst_data_unref (GST_DATA (current_list->data));
202           current_list->data = buffer;
203           buffer = NULL;
204           break;
205         }
206       } else if (GST_BUFFER_OFFSET (current) > GST_BUFFER_OFFSET (buffer)) {
207         GList *previous = g_list_previous (current_list);
208         guint64 start_offset = previous ? 
209                 GST_BUFFER_OFFSET (previous->data) + GST_BUFFER_SIZE (previous->data) : 0;
210
211         if (start_offset == GST_BUFFER_OFFSET (current)) {
212           buffer = NULL;
213           break;
214         } else {
215           /* we have data to insert */
216           if (start_offset > GST_BUFFER_OFFSET (buffer) ||
217               GST_BUFFER_OFFSET (buffer) + GST_BUFFER_SIZE (buffer) > GST_BUFFER_OFFSET (current)) {
218             /* need a subbuffer */
219             start_offset = GST_BUFFER_OFFSET (buffer) > start_offset ? 0 : 
220                            start_offset - GST_BUFFER_OFFSET (buffer);
221             GstBuffer* sub = gst_buffer_create_sub (buffer, start_offset,
222                     MIN (GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (current) - start_offset - GST_BUFFER_OFFSET (buffer)));
223             g_assert (sub);
224             GST_BUFFER_OFFSET (sub) = start_offset + GST_BUFFER_OFFSET (buffer);
225             buffer = sub;
226           } else {
227             gst_data_ref (GST_DATA (buffer));
228           }
229           GST_INFO_OBJECT (store, "adding buffer %p with offset %"G_GINT64_FORMAT" and size %u", 
230                            buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
231           store->buffers = g_list_insert_before (store->buffers, current_list, buffer);
232           buffer = NULL;
233           break;
234         }
235       }
236     }
237     if (buffer) {
238       gst_data_ref (GST_DATA (buffer));
239       GST_INFO_OBJECT (store, "adding buffer %p with offset %"G_GINT64_FORMAT" and size %u", 
240                        buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
241       if (current_list) {
242         g_list_append (current_list, buffer);
243       } else {
244         g_assert (store->buffers == NULL);
245         store->buffers = g_list_prepend (NULL, buffer);
246       }
247     }
248     return TRUE;
249   }
250 }
251 static void
252 gst_buffer_store_cleared_func (GstBufferStore *store)
253 {
254   g_list_foreach (store->buffers, (GFunc) gst_data_unref, NULL);
255   g_list_free (store->buffers);
256   store->buffers = NULL;
257 }
258 /**
259  * gst_buffer_store_new:
260  *
261  * Creates a new bufferstore.
262  *
263  * Returns: the new bufferstore.
264  */
265 GstBufferStore *
266 gst_buffer_store_new (void)
267 {
268   return GST_BUFFER_STORE (g_object_new (GST_TYPE_BUFFER_STORE, NULL));
269 }
270 /**
271  * gst_buffer_store_clear:
272  * @store: a bufferstore
273  *
274  * Clears the buffer store. All buffers are removed and the buffer store
275  * behaves like it was just created.
276  */
277 /* FIXME: call this function _reset ? */
278 void
279 gst_buffer_store_clear (GstBufferStore *store)
280 {
281   g_return_if_fail (GST_IS_BUFFER_STORE (store));
282   
283   g_signal_emit (store, gst_buffer_store_signals [CLEARED], 0, NULL);
284 }
285 /**
286  * gst_buffer_store_add_buffer:
287  * @store: a bufferstore
288  * @buffer: the buffer to add
289  *
290  * Adds a buffer to the buffer store. 
291  *
292  * Returns: TRUE, if the buffer was added, FALSE if an error occured.
293  */
294 gboolean
295 gst_buffer_store_add_buffer (GstBufferStore *store, GstBuffer *buffer)
296 {
297   gboolean ret;
298   
299   g_return_val_if_fail (GST_IS_BUFFER_STORE (store), FALSE);
300   g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
301
302   if (store->buffers &&
303       GST_BUFFER_OFFSET_IS_VALID (store->buffers->data) &&
304       !GST_BUFFER_OFFSET_IS_VALID (buffer))
305     return FALSE;
306   
307   g_signal_emit (store, gst_buffer_store_signals [BUFFER_ADDED], 0, buffer, &ret);
308   
309   return ret;
310 }
311 /**
312  * gst_buffer_store_get_buffer:
313  * @store: a bufferstore
314  * @offset: starting offset of returned buffer
315  * @size: size of returned buffer
316  *
317  * Returns a buffer that corresponds to the given area of data. If part of the
318  * data is not available inside the store, NULL is returned. You have to unref
319  * the buffer after use.
320  *
321  * Returns: a buffer with the requested data or NULL if the data was not 
322  *          available.
323  */
324 GstBuffer *
325 gst_buffer_store_get_buffer (GstBufferStore *store, guint64 offset, guint size)
326 {
327   GstBuffer *current;
328   GList *walk;
329   guint8 *data;
330   guint tmp;
331   gboolean have_offset;
332   guint64 cur_offset = 0;
333   GstBuffer *ret = NULL;
334
335   g_return_val_if_fail (GST_IS_BUFFER_STORE (store), NULL);
336
337   walk = store->buffers;
338   if (!walk)
339     return NULL;
340   if (GST_BUFFER_OFFSET_IS_VALID (walk->data)) {
341     have_offset = TRUE;
342   } else {
343     have_offset = FALSE;
344   }
345   while (walk) {
346     current = GST_BUFFER (walk->data);
347     if (have_offset) {
348       cur_offset = GST_BUFFER_OFFSET (current);
349     }
350     walk = g_list_next (walk);
351     if (cur_offset > offset) {
352       /* #include <windows.h>
353          do_nothing_loop (); */
354     } else if (cur_offset == offset &&
355         GST_BUFFER_SIZE (current) == size) {
356       GST_LOG_OBJECT (store, "found matching buffer %p for offset %"G_GUINT64_FORMAT" and size %u",
357                       current, offset, size);
358       ret = current;
359       gst_data_ref (GST_DATA (ret));
360       GST_LOG_OBJECT (store, "refcount %d",
361                       GST_DATA_REFCOUNT_VALUE(ret));
362       break;
363     } else if (cur_offset + GST_BUFFER_SIZE (current) > offset) {
364       if (cur_offset + GST_BUFFER_SIZE (current) >= offset + size) {
365         ret = gst_buffer_create_sub (current, offset - cur_offset, size);
366         GST_LOG_OBJECT (store, "created subbuffer %p from buffer %p for offset %llu and size %u",
367                         ret, current,  offset, size);
368         break;
369       }
370       /* uh, the requested data spans some buffers */
371       ret = gst_buffer_new_and_alloc (size);
372       GST_LOG_OBJECT (store, "created buffer %p for offset %"G_GUINT64_FORMAT
373                       " and size %u, will fill with data now",
374                       ret, offset, size);
375       data = GST_BUFFER_DATA (ret);
376       tmp = GST_BUFFER_SIZE (current) - offset + cur_offset;
377       memcpy (data, GST_BUFFER_DATA (current) + offset - cur_offset, tmp);
378       data += tmp;
379       size -= tmp;
380       while (size) {
381         if (walk == NULL || 
382             (have_offset && 
383              GST_BUFFER_OFFSET (current) + GST_BUFFER_SIZE (current) != GST_BUFFER_OFFSET (walk->data))) {
384           GST_DEBUG_OBJECT (store, "not all data for offset %"G_GUINT64_FORMAT" and remaining size %u available, aborting",
385                             offset, size);
386           gst_data_unref (GST_DATA (ret));
387           ret = NULL;
388           goto out;
389         }
390         current = GST_BUFFER (walk->data);
391         walk = g_list_next (walk);
392         tmp = MIN (GST_BUFFER_SIZE (current), size);
393         memcpy (data, GST_BUFFER_DATA (current), tmp);
394         size -= tmp;
395       }
396     }
397     if (!have_offset) {
398       cur_offset += GST_BUFFER_SIZE (current);
399     }
400   }
401 out:
402   
403   return ret;
404 }
405 /**
406  * gst_buffer_store_get_size:
407  * @store: a bufferstore
408  * @offset: desired offset
409  *
410  * Calculates the number of bytes available starting from offset. This allows
411  * to query a buffer with the returned size.
412  *
413  * Returns: the number of continuous bytes in the bufferstore starting at
414  *          offset.
415  */
416 guint
417 gst_buffer_store_get_size (GstBufferStore *store, guint64 offset)
418 {
419   GList *walk;
420   gboolean have_offset;
421   gboolean counting = FALSE;
422   guint64 cur_offset = 0;
423   GstBuffer *current = NULL;
424   guint ret = 0;
425
426   g_return_val_if_fail (GST_IS_BUFFER_STORE (store), 0);
427
428   walk = store->buffers;
429   if (!walk)
430     return 0;
431   if (GST_BUFFER_OFFSET_IS_VALID (walk->data)) {
432     have_offset = TRUE;
433   } else {
434     have_offset = FALSE;
435   }
436   while (walk) {
437     if (have_offset && counting && 
438         cur_offset + GST_BUFFER_SIZE (current) !=  GST_BUFFER_OFFSET (walk->data)) {
439       break;
440     }
441     current = GST_BUFFER (walk->data);
442     if (have_offset) {
443       cur_offset = GST_BUFFER_OFFSET (current);
444     }
445     walk = g_list_next (walk);
446     if (counting) {
447       ret += GST_BUFFER_SIZE (current);
448     } else {
449       if (cur_offset > offset)
450         return 0;
451       if (cur_offset + GST_BUFFER_SIZE (current) > offset) {
452         /* we have at least some bytes */
453         ret = cur_offset + GST_BUFFER_SIZE (current) - offset;
454         counting = TRUE;
455       }
456     }
457     if (!have_offset) {
458       cur_offset += GST_BUFFER_SIZE (current);
459     }
460   }
461   
462   return ret;
463 }