diff --git a/libs/gst/base/gstadapter.c b/libs/gst/base/gstadapter.c index 33574981bf..e956948e28 100644 --- a/libs/gst/base/gstadapter.c +++ b/libs/gst/base/gstadapter.c @@ -271,10 +271,13 @@ copy_into_unchecked (GstAdapter * adapter, guint8 * dest, guint skip, while (size > 0) { g = g_slist_next (g); buf = g->data; - csize = MIN (GST_BUFFER_SIZE (buf), size); - memcpy (dest, GST_BUFFER_DATA (buf), csize); - size -= csize; - dest += csize; + bsize = GST_BUFFER_SIZE (buf); + if (G_LIKELY (bsize > 0)) { + csize = MIN (bsize, size); + memcpy (dest, GST_BUFFER_DATA (buf), csize); + size -= csize; + dest += csize; + } } } @@ -285,8 +288,6 @@ copy_into_unchecked (GstAdapter * adapter, guint8 * dest, guint skip, * * Adds the data from @buf to the data stored inside @adapter and takes * ownership of the buffer. - * Empty buffers will be automatically dereferenced and not stored in the - * @adapter. */ void gst_adapter_push (GstAdapter * adapter, GstBuffer * buf) @@ -297,27 +298,19 @@ gst_adapter_push (GstAdapter * adapter, GstBuffer * buf) g_return_if_fail (GST_IS_BUFFER (buf)); size = GST_BUFFER_SIZE (buf); + adapter->size += size; - if (G_UNLIKELY (size == 0)) { - /* we can't have empty buffers, several parts in this file rely on it, this - * has some problems for the timestamp tracking. */ - GST_LOG_OBJECT (adapter, "discarding empty buffer"); - gst_buffer_unref (buf); + /* Note: merging buffers at this point is premature. */ + if (G_UNLIKELY (adapter->buflist == NULL)) { + GST_LOG_OBJECT (adapter, "pushing first %u bytes", size); + adapter->buflist = adapter->buflist_end = g_slist_append (NULL, buf); + update_timestamp (adapter, buf); } else { - adapter->size += size; - - /* Note: merging buffers at this point is premature. */ - if (G_UNLIKELY (adapter->buflist == NULL)) { - GST_LOG_OBJECT (adapter, "pushing first %u bytes", size); - adapter->buflist = adapter->buflist_end = g_slist_append (NULL, buf); - update_timestamp (adapter, buf); - } else { - /* Otherwise append to the end, and advance our end pointer */ - GST_LOG_OBJECT (adapter, "pushing %u bytes at end, size now %u", size, - adapter->size); - adapter->buflist_end = g_slist_append (adapter->buflist_end, buf); - adapter->buflist_end = g_slist_next (adapter->buflist_end); - } + /* Otherwise append to the end, and advance our end pointer */ + GST_LOG_OBJECT (adapter, "pushing %u bytes at end, size now %u", size, + adapter->size); + adapter->buflist_end = g_slist_append (adapter->buflist_end, buf); + adapter->buflist_end = g_slist_next (adapter->buflist_end); } } @@ -711,22 +704,29 @@ gst_adapter_available (GstAdapter * adapter) guint gst_adapter_available_fast (GstAdapter * adapter) { - GstBuffer *first; + GstBuffer *cur; guint size; + GSList *g; g_return_val_if_fail (GST_IS_ADAPTER (adapter), 0); - /* no buffers, we have no data */ - if (!adapter->buflist) + /* no data */ + if (adapter->size == 0) return 0; /* some stuff we already assembled */ if (adapter->assembled_len) return adapter->assembled_len; - /* take the first buffer and its size */ - first = GST_BUFFER_CAST (adapter->buflist->data); - size = GST_BUFFER_SIZE (first); + /* take the first non-zero buffer */ + g = adapter->buflist; + while (TRUE) { + cur = g->data; + size = GST_BUFFER_SIZE (cur); + if (size != 0) + break; + g = g_slist_next (g); + } /* we can quickly get the (remaining) data of the first buffer */ return size - adapter->skip; diff --git a/tests/check/libs/adapter.c b/tests/check/libs/adapter.c index e5a371ead8..0c3ebd3b5e 100644 --- a/tests/check/libs/adapter.c +++ b/tests/check/libs/adapter.c @@ -335,6 +335,8 @@ GST_START_TEST (test_timestamp) guint avail; GstClockTime timestamp; guint64 dist; + guint8 *data; + const guint8 *cdata; adapter = gst_adapter_new (); fail_unless (adapter != NULL); @@ -448,6 +450,85 @@ GST_START_TEST (test_timestamp) fail_unless (timestamp == GST_CLOCK_TIME_NONE); fail_unless (dist == 0); + /* push an empty buffer with timestamp in the adapter */ + buffer = gst_buffer_new (); + GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; + gst_adapter_push (adapter, buffer); + avail = gst_adapter_available (adapter); + fail_unless (avail == 0); + timestamp = gst_adapter_prev_timestamp (adapter, &dist); + fail_unless (timestamp == 2 * GST_SECOND); + fail_unless (dist == 0); + + /* push another empty buffer */ + buffer = gst_buffer_new (); + GST_BUFFER_TIMESTAMP (buffer) = 3 * GST_SECOND; + gst_adapter_push (adapter, buffer); + avail = gst_adapter_available (adapter); + fail_unless (avail == 0); + timestamp = gst_adapter_prev_timestamp (adapter, &dist); + fail_unless (timestamp == 2 * GST_SECOND); + fail_unless (dist == 0); + + /* push a buffer with timestamp in the adapter */ + buffer = gst_buffer_new_and_alloc (100); + GST_BUFFER_TIMESTAMP (buffer) = 4 * GST_SECOND; + gst_adapter_push (adapter, buffer); + avail = gst_adapter_available (adapter); + fail_unless (avail == 100); + timestamp = gst_adapter_prev_timestamp (adapter, &dist); + fail_unless (timestamp == 2 * GST_SECOND); + fail_unless (dist == 0); + + gst_adapter_flush (adapter, 1); + avail = gst_adapter_available (adapter); + fail_unless (avail == 99); + timestamp = gst_adapter_prev_timestamp (adapter, &dist); + fail_unless (timestamp == 4 * GST_SECOND); + fail_unless (dist == 1); + + /* push an empty buffer with timestamp in the adapter */ + buffer = gst_buffer_new (); + GST_BUFFER_TIMESTAMP (buffer) = 5 * GST_SECOND; + gst_adapter_push (adapter, buffer); + avail = gst_adapter_available (adapter); + fail_unless (avail == 99); + timestamp = gst_adapter_prev_timestamp (adapter, &dist); + fail_unless (timestamp == 4 * GST_SECOND); + fail_unless (dist == 1); + + /* push buffer without timestamp */ + buffer = gst_buffer_new_and_alloc (100); + gst_adapter_push (adapter, buffer); + avail = gst_adapter_available (adapter); + fail_unless (avail == 199); + timestamp = gst_adapter_prev_timestamp (adapter, &dist); + fail_unless (timestamp == 4 * GST_SECOND); + fail_unless (dist == 1); + + /* remove first buffer, timestamp of empty buffer is visible */ + buffer = gst_adapter_take_buffer (adapter, 99); + fail_unless (buffer != NULL); + fail_unless (GST_BUFFER_SIZE (buffer) == 99); + gst_buffer_unref (buffer); + avail = gst_adapter_available (adapter); + fail_unless (avail == 100); + timestamp = gst_adapter_prev_timestamp (adapter, &dist); + fail_unless (timestamp == 5 * GST_SECOND); + fail_unless (dist == 0); + + /* remove empty buffer, timestamp still visible */ + cdata = gst_adapter_peek (adapter, 50); + fail_unless (cdata != NULL); + data = gst_adapter_take (adapter, 50); + fail_unless (data != NULL); + g_free (data); + avail = gst_adapter_available (adapter); + fail_unless (avail == 50); + timestamp = gst_adapter_prev_timestamp (adapter, &dist); + fail_unless (timestamp == 5 * GST_SECOND); + fail_unless (dist == 50); + g_object_unref (adapter); }