[Spice-devel] [spice-server 1/3] encoders: Introduce RedChunkIterator
Li Zhijian
lizhijian at cn.fujitsu.com
Mon Sep 12 09:52:31 UTC 2016
On 09/09/2016 09:37 PM, Christophe Fergeau wrote:
> 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)) {
This line seems exceed 100 characters
> + 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)
exceed 100 characters
if we can introduce the checkpatch.pl like QEMU would be cool. :)
Thanks
Zhijian
> +{
> + 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();
> +}
>
--
Best regards.
Li Zhijian (8555)
More information about the Spice-devel
mailing list