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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel