From 4ceea3e44e4debcc2c5c15e89645222c0c0bd4f0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 4 May 2006 14:19:53 +0000 Subject: [PATCH] docs/libs/gstreamer-libs-sections.txt: API: addition: gst_adapter_take_buffer() Original commit message from CVS: * docs/libs/gstreamer-libs-sections.txt: API: addition: gst_adapter_take_buffer() * libs/gst/base/gstadapter.c: (gst_adapter_push), (gst_adapter_peek), (gst_adapter_take), (gst_adapter_take_buffer), (gst_adapter_available_fast): * libs/gst/base/gstadapter.h: Prepare for optimizing the hell out of this hugely inefficient piece of code. Added gst_adapter_take_buffer() so we can at least start thinking about subbuffering and merging. Added some comments. * tests/check/Makefile.am: * tests/check/libs/adapter.c: (GST_START_TEST), (gst_adapter_suite), (main): Added GstAdapter check. --- ChangeLog | 20 +++ docs/libs/gstreamer-libs-sections.txt | 1 + libs/gst/base/gstadapter.c | 62 +++++++- libs/gst/base/gstadapter.h | 3 +- tests/check/Makefile.am | 4 + tests/check/libs/adapter.c | 206 ++++++++++++++++++++++++++ 6 files changed, 291 insertions(+), 5 deletions(-) create mode 100644 tests/check/libs/adapter.c diff --git a/ChangeLog b/ChangeLog index a703838b62..a8c2e4c0ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2006-05-04 Wim Taymans + + * docs/libs/gstreamer-libs-sections.txt: + API: addition: gst_adapter_take_buffer() + + * libs/gst/base/gstadapter.c: (gst_adapter_push), + (gst_adapter_peek), (gst_adapter_take), (gst_adapter_take_buffer), + (gst_adapter_available_fast): + * libs/gst/base/gstadapter.h: + Prepare for optimizing the hell out of this hugely inefficient + piece of code. + Added gst_adapter_take_buffer() so we can at least start thinking + about subbuffering and merging. + Added some comments. + + * tests/check/Makefile.am: + * tests/check/libs/adapter.c: (GST_START_TEST), + (gst_adapter_suite), (main): + Added GstAdapter check. + 2006-05-04 Wim Taymans * docs/design/part-overview.txt: diff --git a/docs/libs/gstreamer-libs-sections.txt b/docs/libs/gstreamer-libs-sections.txt index 3ec14549ca..c22be80fa2 100644 --- a/docs/libs/gstreamer-libs-sections.txt +++ b/docs/libs/gstreamer-libs-sections.txt @@ -112,6 +112,7 @@ gst_adapter_flush gst_adapter_available gst_adapter_available_fast gst_adapter_take +gst_adapter_take_buffer GstAdapterClass GST_ADAPTER diff --git a/libs/gst/base/gstadapter.c b/libs/gst/base/gstadapter.c index f2af1a22f4..017922487f 100644 --- a/libs/gst/base/gstadapter.c +++ b/libs/gst/base/gstadapter.c @@ -199,6 +199,8 @@ gst_adapter_push (GstAdapter * adapter, GstBuffer * buf) g_return_if_fail (GST_IS_BUFFER (buf)); adapter->size += GST_BUFFER_SIZE (buf); + /* FIXME, _append does not scale. Note: merging buffers at this + * point is premature. */ adapter->buflist = g_slist_append (adapter->buflist, buf); } @@ -216,7 +218,8 @@ gst_adapter_push (GstAdapter * adapter, GstBuffer * buf) * of its chain function, the buffer will have an invalid data pointer after * your element flushes the bytes. In that case you should use * gst_adapter_take(), which returns a freshly-allocated buffer that you can set - * as #GstBuffer malloc_data. + * as #GstBuffer malloc_data or the potentially more performant + * gst_adapter_take_buffer(). * * Returns #NULL if @size bytes are not available. * @@ -232,8 +235,10 @@ gst_adapter_peek (GstAdapter * adapter, guint size) g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); g_return_val_if_fail (size > 0, NULL); - /* we don't have enough data, return NULL */ - if (size > adapter->size) + /* we don't have enough data, return NULL. This is unlikely + * as one usually does a _available() first instead of peeking a + * random size. */ + if (G_UNLIKELY (size > adapter->size)) return NULL; /* we have enough assembled data, return it */ @@ -256,6 +261,7 @@ gst_adapter_peek (GstAdapter * adapter, guint size) copied = GST_BUFFER_SIZE (cur) - adapter->skip; memcpy (adapter->assembled_data, GST_BUFFER_DATA (cur) + adapter->skip, copied); + cur_list = g_slist_next (adapter->buflist); while (copied < size) { g_assert (cur_list); @@ -330,7 +336,6 @@ gst_adapter_take (GstAdapter * adapter, guint nbytes) GST_LOG_OBJECT (adapter, "taking %u bytes", nbytes); cdata = gst_adapter_peek (adapter, nbytes); - if (!cdata) return NULL; @@ -342,6 +347,48 @@ gst_adapter_take (GstAdapter * adapter, guint nbytes) return data; } +/** + * gst_adapter_take_buffer: + * @adapter: a #GstAdapter + * @nbytes: the number of bytes to take + * + * Returns a #GstBuffer containing the first @nbytes bytes of the + * @adapter. The returned bytes will be flushed from the adapter. + * This function is potentially more performant that gst_adapter_take() + * since it can reuse the memory in the pushed buffer by subbuffering + * or merging. + * + * Caller owns returned value. gst_buffer_unref() after usage. + * + * Since: 0.10.6 + * + * Returns: a #GstBuffer containing the first @nbytes of the adapter, + * or #NULL if @nbytes bytes are not available + */ +GstBuffer * +gst_adapter_take_buffer (GstAdapter * adapter, guint nbytes) +{ + GstBuffer *buffer; + guint8 *data; + + g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); + g_return_val_if_fail (nbytes > 0, NULL); + + GST_LOG_OBJECT (adapter, "taking buffer of %u bytes", nbytes); + + /* FIXME, optimize me */ + data = gst_adapter_take (adapter, nbytes); + if (data == NULL) + return NULL; + + buffer = gst_buffer_new (); + GST_BUFFER_DATA (buffer) = data; + GST_BUFFER_MALLOCDATA (buffer) = data; + GST_BUFFER_SIZE (buffer) = nbytes; + + return buffer; +} + /** * gst_adapter_available: * @adapter: a #GstAdapter @@ -374,10 +421,17 @@ gst_adapter_available_fast (GstAdapter * adapter) { g_return_val_if_fail (GST_IS_ADAPTER (adapter), 0); + /* no buffers, we have no data */ if (!adapter->buflist) return 0; + + /* some stuff we already assembled */ if (adapter->assembled_len) return adapter->assembled_len; + + /* we cannot have skipped more than the first buffer */ g_assert (GST_BUFFER_SIZE (adapter->buflist->data) > adapter->skip); + + /* we can quickly get the data of the first buffer */ return GST_BUFFER_SIZE (adapter->buflist->data) - adapter->skip; } diff --git a/libs/gst/base/gstadapter.h b/libs/gst/base/gstadapter.h index 32b46a4a78..36492fbf43 100644 --- a/libs/gst/base/gstadapter.h +++ b/libs/gst/base/gstadapter.h @@ -76,7 +76,8 @@ void gst_adapter_clear (GstAdapter *adapter); void gst_adapter_push (GstAdapter *adapter, GstBuffer* buf); const guint8 * gst_adapter_peek (GstAdapter *adapter, guint size); void gst_adapter_flush (GstAdapter *adapter, guint flush); -guint8* gst_adapter_take (GstAdapter * adapter, guint nbytes); +guint8* gst_adapter_take (GstAdapter *adapter, guint nbytes); +GstBuffer* gst_adapter_take_buffer (GstAdapter *adapter, guint nbytes); guint gst_adapter_available (GstAdapter *adapter); guint gst_adapter_available_fast (GstAdapter *adapter); GType gst_adapter_get_type (void); diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 309d579289..95a1e37fe2 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -78,6 +78,7 @@ check_PROGRAMS = \ $(REGISTRY_CHECKS) \ libs/libsabi \ libs/gdp \ + libs/adapter \ libs/gstnetclientclock \ libs/gstnettimeprovider @@ -104,6 +105,9 @@ elements_fdsrc_CFLAGS=$(GST_OBJ_CFLAGS) $(CHECK_CFLAGS) -DTESTFILE=\"$(top_srcdi libs_basesrc_LDADD = \ $(top_builddir)/libs/gst/base/libgstbase-@GST_MAJORMINOR@.la \ $(LDADD) +libs_adapter_LDADD = \ + $(top_builddir)/libs/gst/base/libgstbase-@GST_MAJORMINOR@.la \ + $(LDADD) libs_controller_LDADD = \ $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la \ $(LDADD) diff --git a/tests/check/libs/adapter.c b/tests/check/libs/adapter.c new file mode 100644 index 0000000000..80d8e70e20 --- /dev/null +++ b/tests/check/libs/adapter.c @@ -0,0 +1,206 @@ +/* GStreamer + * + * unit test for adapter + * + * Copyright (C) <2005> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include + +/* does some implementation dependent checking that should + * also be optimal + */ + +/* + * Start peeking on an adapter with 1 buffer pushed. + */ +GST_START_TEST (test_peek1) +{ + GstAdapter *adapter; + GstBuffer *buffer; + guint avail; + const guint8 *bufdata, *data1, *data2; + + adapter = gst_adapter_new (); + fail_if (adapter == NULL); + + /* push single buffer in adapter */ + buffer = gst_buffer_new_and_alloc (512); + bufdata = GST_BUFFER_DATA (buffer); + + fail_if (buffer == NULL); + gst_adapter_push (adapter, buffer); + + /* available and available_fast should return the size of the + * buffer */ + avail = gst_adapter_available (adapter); + fail_if (avail != 512); + avail = gst_adapter_available_fast (adapter); + fail_if (avail != 512); + + /* should g_critical with NULL as result */ + ASSERT_CRITICAL (data1 = gst_adapter_peek (adapter, 0)); + fail_if (data1 != NULL); + + /* should return NULL as result */ + data1 = gst_adapter_peek (adapter, 513); + fail_if (data1 != NULL); + + /* this should work */ + data1 = gst_adapter_peek (adapter, 512); + fail_if (data1 == NULL); + /* it should point to the buffer data as well */ + fail_if (data1 != bufdata); + data2 = gst_adapter_peek (adapter, 512); + fail_if (data2 == NULL); + /* second peek should return the same pointer */ + fail_if (data2 != data1); + + /* this should fail since we don't have that many bytes */ + ASSERT_CRITICAL (gst_adapter_flush (adapter, 513)); + + /* this should work fine */ + gst_adapter_flush (adapter, 10); + + /* see if we have 10 bytes less available */ + avail = gst_adapter_available (adapter); + fail_if (avail != 502); + avail = gst_adapter_available_fast (adapter); + fail_if (avail != 502); + + /* should return NULL as result */ + data2 = gst_adapter_peek (adapter, 503); + fail_if (data2 != NULL); + + /* should work fine */ + data2 = gst_adapter_peek (adapter, 502); + fail_if (data2 == NULL); + /* peek should return the same old pointer + 10 */ + fail_if (data2 != data1 + 10); + fail_if (data2 != bufdata + 10); + + /* flush some more */ + gst_adapter_flush (adapter, 500); + + /* see if we have 2 bytes available */ + avail = gst_adapter_available (adapter); + fail_if (avail != 2); + avail = gst_adapter_available_fast (adapter); + fail_if (avail != 2); + + data2 = gst_adapter_peek (adapter, 2); + fail_if (data2 == NULL); + fail_if (data2 != data1 + 510); + fail_if (data2 != bufdata + 510); + + /* flush some more */ + gst_adapter_flush (adapter, 2); + + /* see if we have 0 bytes available */ + avail = gst_adapter_available (adapter); + fail_if (avail != 0); + avail = gst_adapter_available_fast (adapter); + fail_if (avail != 0); + + /* silly clear just for fun */ + gst_adapter_clear (adapter); + + g_object_unref (adapter); +} + +GST_END_TEST; + +/* Start peeking on an adapter with 2 non-mergeable buffers + * pushed. + */ +GST_START_TEST (test_peek2) +{ +} + +GST_END_TEST; + +/* Start peeking on an adapter with 2 mergeable buffers + * pushed. + */ +GST_START_TEST (test_peek3) +{ +} + +GST_END_TEST; + +/* take data from an adapter with 1 buffer pushed. + */ +GST_START_TEST (test_take1) +{ +} + +GST_END_TEST; + +/* take data from an adapter with 2 non-mergeable buffers + * pushed. + */ +GST_START_TEST (test_take2) +{ +} + +GST_END_TEST; + +/* take data from an adapter with 2 mergeable buffers + * pushed. + */ +GST_START_TEST (test_take3) +{ +} + +GST_END_TEST; + +Suite * +gst_adapter_suite (void) +{ + Suite *s = suite_create ("adapter"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_peek1); + tcase_add_test (tc_chain, test_peek2); + tcase_add_test (tc_chain, test_peek3); + tcase_add_test (tc_chain, test_take1); + tcase_add_test (tc_chain, test_take2); + tcase_add_test (tc_chain, test_take3); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_adapter_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +}