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