[Spice-devel] [spice-server 1/3] encoders: Introduce RedChunkIterator

Christophe Fergeau cfergeau at redhat.com
Fri Sep 9 13:37:20 UTC 2016


This will be used by the GStreamer/mjpeg encoders to get linear data out
of a SpiceChunks (which is an array of (binary data, size)).
---
 server/Makefile.am                     |   2 +
 server/red-chunk-iterator.c            | 144 ++++++++++++++++++++++++++
 server/red-chunk-iterator.h            |  46 +++++++++
 server/tests/Makefile.am               |   9 +-
 server/tests/test-red-chunk-iterator.c | 179 +++++++++++++++++++++++++++++++++
 5 files changed, 378 insertions(+), 2 deletions(-)
 create mode 100644 server/red-chunk-iterator.c
 create mode 100644 server/red-chunk-iterator.h
 create mode 100644 server/tests/test-red-chunk-iterator.c

diff --git a/server/Makefile.am b/server/Makefile.am
index d31a9e8..ac576c9 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -156,6 +156,8 @@ libserver_la_SOURCES =				\
 	dcc-private.h				\
 	image-encoders.c					\
 	image-encoders.h					\
+	red-chunk-iterator.c			\
+	red-chunk-iterator.h			\
 	$(NULL)
 
 if HAVE_LZ4
diff --git a/server/red-chunk-iterator.c b/server/red-chunk-iterator.c
new file mode 100644
index 0000000..07b05b2
--- /dev/null
+++ b/server/red-chunk-iterator.c
@@ -0,0 +1,144 @@
+/*
+   Copyright (C) 2016 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(HAVE_GSTREAMER_1_0)
+#include <gst/gst.h>
+#endif
+
+#include "red-chunk-iterator.h"
+
+void red_chunk_iterator_init(RedChunkIterator *chunk_iterator, SpiceChunks *chunks)
+{
+    memset(chunk_iterator, 0, sizeof(RedChunkIterator));
+    chunk_iterator->chunks = chunks;
+}
+
+static SpiceChunk *red_chunk_iterator_get_current_chunk(RedChunkIterator *chunk_iterator)
+{
+    if (chunk_iterator->chunks->num_chunks <= chunk_iterator->current_chunk_index)
+    {
+        return NULL;
+    }
+
+    return &chunk_iterator->chunks->chunk[chunk_iterator->current_chunk_index];
+}
+
+static SpiceChunk *red_chunk_iterator_next_chunk(RedChunkIterator *chunk_iterator)
+{
+    if (chunk_iterator->current_chunk_index < chunk_iterator->chunks->num_chunks) {
+        chunk_iterator->current_chunk_index++;
+    }
+    chunk_iterator->current_chunk_offset = 0;
+
+    return red_chunk_iterator_get_current_chunk(chunk_iterator);
+}
+
+gboolean red_chunk_iterator_skip_bytes(RedChunkIterator *chunk_iterator, gsize skip_bytes)
+{
+    SpiceChunk *current_chunk = red_chunk_iterator_get_current_chunk(chunk_iterator);
+
+    while ((current_chunk != NULL) && (skip_bytes >= current_chunk->len - chunk_iterator->current_chunk_offset)) {
+        skip_bytes -= (current_chunk->len - chunk_iterator->current_chunk_offset);
+        current_chunk = red_chunk_iterator_next_chunk(chunk_iterator);
+    }
+    chunk_iterator->current_chunk_offset += skip_bytes;
+    return (current_chunk != NULL);
+}
+
+static gsize
+red_chunk_iterator_get_data_one_chunk(RedChunkIterator *chunk_iterator, guint8 *dst, gsize bytes_count)
+{
+    SpiceChunk *current_chunk = red_chunk_iterator_get_current_chunk(chunk_iterator);
+    uint8_t *src;
+    gsize copied_len;
+
+    if (current_chunk == NULL) {
+        return 0;
+    }
+    /* FIXME: check alignemnt ? */
+    /* do some checks on bytes_count before copying */
+    copied_len = MIN(bytes_count, current_chunk->len - chunk_iterator->current_chunk_offset);
+    src = current_chunk->data + chunk_iterator->current_chunk_offset;
+    memcpy(dst, src, copied_len);
+    red_chunk_iterator_skip_bytes(chunk_iterator, copied_len);
+
+    return copied_len;
+}
+
+gsize red_chunk_iterator_get_data(RedChunkIterator *chunk_iterator, guint8 *dst, gsize bytes_count)
+{
+    gsize total_copied = 0;
+
+    while (total_copied < bytes_count) {
+        gsize copied_bytes;
+
+        copied_bytes = red_chunk_iterator_get_data_one_chunk(chunk_iterator, dst,
+                                                             bytes_count - total_copied);
+        if (copied_bytes == 0) {
+            break;
+        }
+        total_copied += copied_bytes;
+        dst += copied_bytes;
+    }
+
+    return total_copied;
+}
+
+gsize red_chunk_iterator_peek_data(RedChunkIterator *chunk_iterator, guint8 *dst, gsize bytes_count)
+{
+    const gsize initial_chunk_index = chunk_iterator->current_chunk_index;
+    const gsize initial_chunk_offset = chunk_iterator->current_chunk_offset;
+    gsize bytes_copied;
+
+    bytes_copied = red_chunk_iterator_get_data(chunk_iterator, dst, bytes_count);
+
+    chunk_iterator->current_chunk_index = initial_chunk_index;
+    chunk_iterator->current_chunk_offset = initial_chunk_offset;
+
+    return bytes_copied;
+}
+
+#if defined(HAVE_GSTREAMER_1_0)
+GstMemory *red_chunk_iterator_get_current_chunk_as_gst(RedChunkIterator *chunk_iterator,
+                                                       gsize bytes_count, gpointer user_data,
+                                                       GDestroyNotify notify)
+{
+    SpiceChunk *current_chunk = red_chunk_iterator_get_current_chunk(chunk_iterator);
+    GstMemory *memory;
+    gsize data_len;
+
+    if (current_chunk == NULL) {
+        return NULL;
+    }
+
+    data_len = current_chunk->len - chunk_iterator->current_chunk_offset;
+    if (bytes_count != 0) {
+        data_len = MIN(data_len, bytes_count);
+    }
+    memory = gst_memory_new_wrapped(GST_MEMORY_FLAG_READONLY,
+                                    current_chunk->data,
+                                    current_chunk->len,
+                                    chunk_iterator->current_chunk_offset, data_len,
+                                    user_data, notify);
+    red_chunk_iterator_skip_bytes(chunk_iterator, data_len);
+
+    return memory;
+}
+#endif
diff --git a/server/red-chunk-iterator.h b/server/red-chunk-iterator.h
new file mode 100644
index 0000000..54b23c8
--- /dev/null
+++ b/server/red-chunk-iterator.h
@@ -0,0 +1,46 @@
+/*
+   Copyright (C) 2016 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_RED_CHUNK_ITERATOR
+#define _H_RED_CHUNK_ITERATOR
+
+#if defined(HAVE_GSTREAMER_1_0)
+#include <gst/gst.h>
+#endif
+
+#include "red-channel.h"
+#include "spice-qxl.h"
+
+typedef struct RedChunkIterator RedChunkIterator;
+
+typedef struct RedChunkIterator {
+    SpiceChunks *chunks;
+    gsize current_chunk_index; /* Index of the chunk to use */
+    gsize current_chunk_offset; /* Byte offset within current chunk */
+} RedChunkIterator;
+
+void red_chunk_iterator_init(RedChunkIterator *chunk_iterator, SpiceChunks *chunks);
+gboolean red_chunk_iterator_skip_bytes(RedChunkIterator *chunk_iterator, gsize skip_bytes);
+gsize red_chunk_iterator_get_data(RedChunkIterator *chunk_iterator, guint8 *dst, gsize bytes_count);
+gsize red_chunk_iterator_peek_data(RedChunkIterator *chunk_iterator, guint8 *dst, gsize bytes_count);
+#if defined(HAVE_GSTREAMER_1_0)
+GstMemory *red_chunk_iterator_get_current_chunk_as_gst(RedChunkIterator *chunk_iterator,
+                                                       gsize bytes_count, gpointer user_data,
+                                                       GDestroyNotify notify);
+#endif
+
+#endif
diff --git a/server/tests/Makefile.am b/server/tests/Makefile.am
index 59a1fac..4c911f5 100644
--- a/server/tests/Makefile.am
+++ b/server/tests/Makefile.am
@@ -8,7 +8,9 @@ AM_CPPFLAGS =					\
 	-I$(top_srcdir)/server/tests		\
 	$(COMMON_CFLAGS)			\
 	$(GLIB2_CFLAGS)				\
-	$(GOBJECT2_CFLAGS)				\
+	$(GOBJECT2_CFLAGS)			\
+	$(GSTREAMER_0_10_CFLAGS)		\
+	$(GSTREAMER_1_0_CFLAGS)			\
 	$(SMARTCARD_CFLAGS)			\
 	$(SPICE_NONPKGCONFIG_CFLAGS)		\
 	$(SPICE_PROTOCOL_CFLAGS)		\
@@ -33,7 +35,9 @@ LDADD =								\
 	$(top_builddir)/server/libserver.la			\
 	$(GLIB2_LIBS)						\
 	$(GOBJECT2_LIBS)					\
-	$(SPICE_NONPKGCONFIG_LIBS)		                \
+	$(GSTREAMER_0_10_LIBS)					\
+	$(GSTREAMER_1_0_LIBS)					\
+	$(SPICE_NONPKGCONFIG_LIBS)				\
 	$(NULL)
 
 TESTS =						\
@@ -42,6 +46,7 @@ TESTS =						\
 	stream-test				\
 	test-loop				\
 	test-qxl-parsing			\
+	test-red-chunk-iterator			\
 	$(NULL)
 
 noinst_PROGRAMS =				\
diff --git a/server/tests/test-red-chunk-iterator.c b/server/tests/test-red-chunk-iterator.c
new file mode 100644
index 0000000..93d8646
--- /dev/null
+++ b/server/tests/test-red-chunk-iterator.c
@@ -0,0 +1,179 @@
+/*
+   Copyright (C) 2016 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "red-chunk-iterator.h"
+
+typedef struct { uint32_t len; uint8_t data[5]; } TestChunk;
+
+static gboolean spice_chunks_add_chunk(SpiceChunks *chunks, gsize chunk_index,
+                                       uint8_t data[], gsize len)
+{
+    if (chunk_index >= chunks->num_chunks) {
+        return FALSE;
+    }
+    //g_warn_if_fail(chunks->chunk[chunk_index].len == 0);
+
+    chunks->chunk[chunk_index].len = len;
+    chunks->chunk[chunk_index].data = data;
+    chunks->data_size += len;
+
+    return TRUE;
+}
+
+
+TestChunk test_chunks[4] = {
+    { .len = 3, .data = { 0x01, 0x02, 0x03 } },
+    { .len = 1, .data = { 0x04 } },
+    { .len = 5, .data = { 0x05, 0x06, 0x07, 0x08, 0x09 } },
+    { .len = 2, .data = { 0x0a, 0x0b } },
+};
+
+static SpiceChunks *build_test_chunks(void)
+{
+    SpiceChunks *chunks;
+    gsize num_chunks = G_N_ELEMENTS(test_chunks);
+
+    chunks = spice_chunks_new(num_chunks);
+    chunks->data_size = 0;
+    for (unsigned int i = 0; i < num_chunks; i++) {
+        spice_chunks_add_chunk(chunks, i, test_chunks[i].data, test_chunks[i].len);
+    }
+
+    return chunks;
+}
+
+static void check_iterator_read(RedChunkIterator *iterator, guint skipped_count, guint read_size)
+{
+    unsigned int current_val = skipped_count + 1;
+
+    while (current_val < iterator->chunks->data_size) {
+        uint8_t data[read_size];
+        gsize copied;
+
+        copied = red_chunk_iterator_get_data(iterator, data, read_size);
+        g_assert_cmpuint(copied, <=, read_size);
+        if (copied != read_size) {
+            g_assert_cmpuint(current_val - 1 + copied, ==, iterator->chunks->data_size);
+        }
+        for (unsigned int i = 0; i < copied; i++) {
+            g_assert_cmpuint(current_val, ==, data[i]);
+            current_val++;
+        }
+    }
+}
+
+static void test_red_chunk_iterator(void)
+{
+    SpiceChunks *chunks;
+
+    chunks = build_test_chunks();
+
+    for (unsigned int skip = 0; skip <= chunks->data_size; skip++) {
+        for (unsigned int read_size = 1; read_size <= chunks->data_size; read_size++) {
+            RedChunkIterator iterator;
+            red_chunk_iterator_init(&iterator, chunks);
+
+            red_chunk_iterator_skip_bytes(&iterator, skip);
+            check_iterator_read(&iterator, skip, 3);
+        }
+    }
+
+    spice_chunks_destroy(chunks);
+}
+
+static void test_red_chunk_iterator_skip(void)
+{
+    SpiceChunks *chunks;
+    RedChunkIterator iterator;
+
+    chunks = build_test_chunks();
+
+    red_chunk_iterator_init(&iterator, chunks);
+    red_chunk_iterator_skip_bytes(&iterator, 1);
+    red_chunk_iterator_skip_bytes(&iterator, 3);
+    check_iterator_read(&iterator, 4, 3);
+
+    red_chunk_iterator_init(&iterator, chunks);
+    red_chunk_iterator_skip_bytes(&iterator, 1);
+    red_chunk_iterator_skip_bytes(&iterator, 1);
+    red_chunk_iterator_skip_bytes(&iterator, 3);
+    check_iterator_read(&iterator, 5, 3);
+
+    red_chunk_iterator_init(&iterator, chunks);
+    red_chunk_iterator_skip_bytes(&iterator, 1);
+    red_chunk_iterator_skip_bytes(&iterator, 4);
+    check_iterator_read(&iterator, 5, 3);
+
+    red_chunk_iterator_init(&iterator, chunks);
+    red_chunk_iterator_skip_bytes(&iterator, 1);
+    red_chunk_iterator_skip_bytes(&iterator, 5);
+    check_iterator_read(&iterator, 6, 3);
+
+    spice_chunks_destroy(chunks);
+}
+
+#if defined(HAVE_GSTREAMER_1_0)
+static void test_red_chunk_iterator_gst(void)
+{
+    SpiceChunks *chunks;
+    RedChunkIterator iterator;
+
+    chunks = build_test_chunks();
+
+    red_chunk_iterator_init(&iterator, chunks);
+    red_chunk_iterator_skip_bytes(&iterator, 1);
+
+    for (unsigned int i = 0; i < G_N_ELEMENTS(test_chunks); i++) {
+        GstMemory *memory;
+        GstMapInfo info;
+        gsize offset = 0;
+
+        if (i == 0) {
+            offset = 1;
+        }
+        memory = red_chunk_iterator_get_current_chunk_as_gst(&iterator, 0, NULL, NULL);
+        g_assert_true(gst_memory_map(memory, &info, GST_MAP_READ));
+        g_assert_cmpuint(test_chunks[i].len - offset, ==, info.size);
+        g_assert_cmpuint(memcmp(test_chunks[i].data + offset, info.data, info.size), ==, 0);
+        gst_memory_unmap(memory, &info);
+        gst_memory_unref(memory);
+    }
+
+    spice_chunks_destroy(chunks);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#if defined(HAVE_GSTREAMER_1_0)
+    gst_init(&argc, &argv);
+#endif
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/spice-server/red-chunk-iterator", test_red_chunk_iterator);
+    g_test_add_func("/spice-server/red-chunk-iterator-skip", test_red_chunk_iterator_skip);
+#if defined(HAVE_GSTREAMER_1_0) || defined(HAVE_GSTREAMER_0_10)
+    g_test_add_func("/spice-server/red-chunk-iterator-gst", test_red_chunk_iterator_gst);
+#endif
+
+    return g_test_run();
+}
-- 
2.7.4



More information about the Spice-devel mailing list