[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