From 076a45acc559f094c52501c7b8832f023a6d0fb8 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 29 Dec 2017 22:48:21 +1100 Subject: [PATCH] [client] added initial decoder framework --- client/Makefile | 6 ++- client/decoders/h264.c | 110 ++++++++++++++++++++++++++++++++++++++ client/decoders/null.c | 97 +++++++++++++++++++++++++++++++++ client/lg-decoder.h | 52 ++++++++++++++++++ client/lg-decoders.h | 33 ++++++++++++ client/main.c | 4 +- client/renderers/opengl.c | 58 +++++++++++++++++--- 7 files changed, 349 insertions(+), 11 deletions(-) create mode 100644 client/decoders/h264.c create mode 100644 client/decoders/null.c create mode 100644 client/lg-decoder.h create mode 100644 client/lg-decoders.h diff --git a/client/Makefile b/client/Makefile index 4f7dab70..9f593c83 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,12 +1,12 @@ BINARY = looking-glass-client -CFLAGS = -g -O3 -std=gnu99 -march=native -Wall -Werror -I./ -I../common -DDEBUG -DATOMIC_LOCKING +CFLAGS = -g -Og -std=gnu99 -march=native -Wall -Werror -I./ -I../common -DDEBUG -DATOMIC_LOCKING LDFLAGS = -lrt CFLAGS += -ffast-math CFLAGS += -fdata-sections -ffunction-sections LDFLAGS += -Wl,--gc-sections -LIBS = sdl2 SDL2_ttf gl glu libssl openssl spice-protocol fontconfig x11 libconfig +LIBS = sdl2 SDL2_ttf gl glu libssl openssl spice-protocol fontconfig x11 libconfig libva CFLAGS += $(shell pkg-config --cflags $(LIBS)) LDFLAGS += $(shell pkg-config --libs $(LIBS)) BUILD ?= .build @@ -17,6 +17,8 @@ CFLAGS += -DBUILD_VERSION='"$(shell git describe --always --long --dirty --abbr OBJS = main.o \ lg-renderer.o \ spice/spice.o \ + decoders/null.o \ + decoders/h264.o \ renderers/opengl.o # renderers/opengl-basic.o diff --git a/client/decoders/h264.c b/client/decoders/h264.c new file mode 100644 index 00000000..7df9e376 --- /dev/null +++ b/client/decoders/h264.c @@ -0,0 +1,110 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "lg-decoder.h" + +#include "debug.h" + +#include +#include + +struct Inst +{ + bool initialized; + + LG_RendererFormat format; +}; + +static bool lgd_h264_create (void ** opaque); +static void lgd_h264_destroy (void * opaque); +static bool lgd_h264_initialize (void * opaque, const LG_RendererFormat format); +static void lgd_h264_deinitialize (void * opaque); +static LG_OutFormat lgd_h264_get_out_format (void * opaque); +static unsigned int lgd_h264_get_frame_pitch(void * opaque); +static bool lgd_h264_decode(void * opaque, uint8_t * dst, size_t dstSize, const uint8_t * src, size_t srcSize); + +static bool lgd_h264_create(void ** opaque) +{ + // create our local storage + *opaque = malloc(sizeof(struct Inst)); + if (!*opaque) + { + DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst)); + return false; + } + memset(*opaque, 0, sizeof(struct Inst)); + + //struct Inst * this = (struct Inst *)*opaque; + + return true; +} + +static void lgd_h264_destroy(void * opaque) +{ + struct Inst * this = (struct Inst *)opaque; + lgd_h264_deinitialize(opaque); + free(this); +} + +static bool lgd_h264_initialize(void * opaque, const LG_RendererFormat format) +{ + struct Inst * this = (struct Inst *)opaque; + if (this->initialized) + lgd_h264_deinitialize(opaque); + + memcpy(&this->format, &format, sizeof(LG_RendererFormat)); + return true; +} + +static void lgd_h264_deinitialize(void * opaque) +{ + struct Inst * this = (struct Inst *)opaque; + if (!this->initialized) + return; + + memset(this, 0, sizeof(struct Inst)); +} + +static LG_OutFormat lgd_h264_get_out_format(void * opaque) +{ + return LG_OUTPUT_BGRA; +} + +static unsigned int lgd_h264_get_frame_pitch(void * opaque) +{ + struct Inst * this = (struct Inst *)opaque; + return this->format.width * 4; +} + +static bool lgd_h264_decode(void * opaque, uint8_t * dst, size_t dstSize, const uint8_t * src, size_t srcSize) +{ + return true; +} + +const LG_Decoder LGD_H264 = +{ + .name = "H.264", + .create = lgd_h264_create, + .destroy = lgd_h264_destroy, + .initialize = lgd_h264_initialize, + .deinitialize = lgd_h264_deinitialize, + .get_out_format = lgd_h264_get_out_format, + .get_frame_pitch = lgd_h264_get_frame_pitch, + .decode = lgd_h264_decode +}; \ No newline at end of file diff --git a/client/decoders/null.c b/client/decoders/null.c new file mode 100644 index 00000000..6834d709 --- /dev/null +++ b/client/decoders/null.c @@ -0,0 +1,97 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "lg-decoder.h" + +#include "debug.h" +#include "memcpySSE.h" + +#include +#include + +struct Inst +{ + LG_RendererFormat format; +}; + +static bool lgd_null_create (void ** opaque); +static void lgd_null_destroy (void * opaque); +static bool lgd_null_initialize (void * opaque, const LG_RendererFormat format); +static void lgd_null_deinitialize (void * opaque); +static LG_OutFormat lgd_null_get_out_format (void * opaque); +static unsigned int lgd_null_get_frame_pitch(void * opaque); +static bool lgd_null_decode (void * opaque, uint8_t * dst, size_t dstSize, const uint8_t * src, size_t srcSize); + +static bool lgd_null_create(void ** opaque) +{ + // create our local storage + *opaque = malloc(sizeof(struct Inst)); + if (!*opaque) + { + DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst)); + return false; + } + memset(*opaque, 0, sizeof(struct Inst)); + return true; +} + +static void lgd_null_destroy(void * opaque) +{ + free(opaque); +} + +static bool lgd_null_initialize(void * opaque, const LG_RendererFormat format) +{ + struct Inst * this = (struct Inst *)opaque; + memcpy(&this->format, &format, sizeof(LG_RendererFormat)); + return true; +} + +static void lgd_null_deinitialize(void * opaque) +{ +} + +static LG_OutFormat lgd_null_get_out_format(void * opaque) +{ + return LG_OUTPUT_BGRA; +} + +static unsigned int lgd_null_get_frame_pitch(void * opaque) +{ + struct Inst * this = (struct Inst *)opaque; + return this->format.pitch; +} + +static bool lgd_null_decode(void * opaque, uint8_t * dst, size_t dstSize, const uint8_t * src, size_t srcSize) +{ + memcpySSE(dst, src, dstSize); + return true; +} + +const LG_Decoder LGD_NULL = +{ + .name = "NULL", + .create = lgd_null_create, + .destroy = lgd_null_destroy, + .initialize = lgd_null_initialize, + .deinitialize = lgd_null_deinitialize, + .get_out_format = lgd_null_get_out_format, + .get_frame_pitch = lgd_null_get_frame_pitch, + .decode = lgd_null_decode +}; \ No newline at end of file diff --git a/client/lg-decoder.h b/client/lg-decoder.h new file mode 100644 index 00000000..7cefeb84 --- /dev/null +++ b/client/lg-decoder.h @@ -0,0 +1,52 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#pragma once + +#include +#include + +#include "lg-renderer.h" + +typedef enum LG_OutFormat +{ + LG_OUTPUT_BGRA +} +LG_OutFormat; + +typedef bool (* LG_DecoderCreate )(void ** opaque); +typedef void (* LG_DecoderDestroy )(void * opaque); +typedef bool (* LG_DecoderInitialize )(void * opaque, const LG_RendererFormat format); +typedef void (* LG_DecoderDeInitialize )(void * opaque); +typedef LG_OutFormat (* LG_DecoderGetOutFormat )(void * opaque); +typedef unsigned int (* LG_DecoderGetFramePitch)(void * opaque); +typedef bool (* LG_DecoderDecode )(void * opaque, uint8_t * dst, size_t dstSize, const uint8_t * src, size_t srcSize); + +typedef struct LG_Decoder +{ + const char * name; + LG_DecoderCreate create; + LG_DecoderDestroy destroy; + LG_DecoderInitialize initialize; + LG_DecoderDeInitialize deinitialize; + LG_DecoderGetOutFormat get_out_format; + LG_DecoderGetFramePitch get_frame_pitch; + LG_DecoderDecode decode; +} +LG_Decoder; \ No newline at end of file diff --git a/client/lg-decoders.h b/client/lg-decoders.h new file mode 100644 index 00000000..d33fe89f --- /dev/null +++ b/client/lg-decoders.h @@ -0,0 +1,33 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#pragma once +#include "lg-decoder.h" + +extern const LG_Decoder LGD_H264; +extern const LG_Decoder LGD_NULL; + +const LG_Decoder * LG_Decoders[] = +{ + &LGD_NULL, + &LGD_H264, + NULL // end of array sentinal +}; + +#define LG_DECODER_COUNT ((sizeof(LG_Decoders) / sizeof(LG_Decoder *)) - 1) \ No newline at end of file diff --git a/client/main.c b/client/main.c index dbdf0bd4..4cc5e869 100644 --- a/client/main.c +++ b/client/main.c @@ -325,14 +325,17 @@ int frameThread(void * unused) lgrFormat.stride = header.detail.frame.stride; lgrFormat.pitch = header.detail.frame.pitch; + size_t dataSize; switch(header.detail.frame.type) { case FRAME_TYPE_ARGB: + dataSize = lgrFormat.height * lgrFormat.pitch; lgrFormat.comp = LG_COMPRESSION_NONE; lgrFormat.bpp = 32; break; case FRAME_TYPE_H264: + dataSize = lgrFormat.pitch; lgrFormat.comp = LG_COMPRESSION_H264; lgrFormat.bpp = 0; break; @@ -347,7 +350,6 @@ int frameThread(void * unused) break; // check the header's dataPos is sane - const size_t dataSize = lgrFormat.height * lgrFormat.pitch; if (header.detail.frame.dataPos + dataSize > state.shmSize) { DEBUG_ERROR("The guest sent an invalid dataPos"); diff --git a/client/renderers/opengl.c b/client/renderers/opengl.c index 93c978d7..233d4462 100644 --- a/client/renderers/opengl.c +++ b/client/renderers/opengl.c @@ -32,6 +32,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "debug.h" #include "memcpySSE.h" #include "utils.h" +#include "lg-decoders.h" #define BUFFER_COUNT 2 @@ -76,6 +77,8 @@ struct Inst GLuint intFormat; GLuint vboFormat; size_t texSize; + const LG_Decoder* decoder; + void * decoderData; uint64_t drawStart; bool hasBuffers; @@ -282,7 +285,18 @@ bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const // lock, perform the update, then unlock LG_LOCK(this->syncLock); - memcpySSE(this->texPixels[this->wTexIndex], data, this->texSize); + if (!this->decoder->decode( + this->decoderData, + this->texPixels[this->wTexIndex], + this->texSize, + data, + this->format.pitch + )) + { + DEBUG_ERROR("decode returned failure"); + LG_UNLOCK(this->syncLock); + return false; + } this->frameUpdate = true; LG_UNLOCK(this->syncLock); @@ -552,30 +566,52 @@ static bool configure(struct Inst * this, SDL_Window *window) switch(this->format.comp) { case LG_COMPRESSION_NONE: + this->decoder = &LGD_NULL; break; case LG_COMPRESSION_H264: - DEBUG_INFO("h264 not supported yet"); - LG_UNLOCK(this->formatLock); + this->decoder = &LGD_H264; + break; + + default: + DEBUG_ERROR("Unknown/unsupported compression type"); return false; } - // assume 32 bit formats are BGRA - switch(this->format.bpp) + DEBUG_INFO("Using decoder: %s", this->decoder->name); + + if (!this->decoder->create(&this->decoderData)) { - case 32: + DEBUG_ERROR("Failed to create the decoder"); + return false; + } + + if (!this->decoder->initialize( + this->decoderData, + this->format + )) + { + DEBUG_ERROR("Failed to initialize decoder"); + return false; + } + + switch(this->decoder->get_out_format(this->decoderData)) + { + case LG_OUTPUT_BGRA: this->intFormat = GL_RGBA8; this->vboFormat = GL_BGRA; break; default: - DEBUG_INFO("%d bpp not supported", this->format.bpp); + DEBUG_ERROR("Format not supported"); LG_UNLOCK(this->formatLock); return false; } // calculate the texture size in bytes - this->texSize = this->format.height * this->format.pitch; + this->texSize = + this->format.height * + this->decoder->get_frame_pitch(this->decoderData); // generate lists for drawing this->texList = glGenLists(BUFFER_COUNT); @@ -729,6 +765,12 @@ static void deconfigure(struct Inst * this) this->glContext = NULL; } + if (this->decoder) + { + this->decoder->destroy(this->decoderData); + this->decoderData = NULL; + } + this->configured = false; }