From 05bd587c740503ae87ee57603ac656030f226b3b Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sat, 28 Jul 2018 08:41:15 +1000 Subject: [PATCH] [client] implemented initial slow yuv420 support --- client/CMakeLists.txt | 1 + client/decoders/yuv420.c | 172 ++++++++++++++++++++++++++++++++++++++ client/lg-decoders.h | 2 + client/lg-renderer.h | 21 ++--- client/main.c | 13 ++- client/renderers/opengl.c | 10 ++- 6 files changed, 199 insertions(+), 20 deletions(-) create mode 100644 client/decoders/yuv420.c diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index c1e4c198..cf1347f7 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -53,6 +53,7 @@ set(SOURCES spice/rsa.c spice/spice.c decoders/null.c + decoders/yuv420.c renderers/opengl.c ) diff --git a/client/decoders/yuv420.c b/client/decoders/yuv420.c new file mode 100644 index 00000000..3c81e8ce --- /dev/null +++ b/client/decoders/yuv420.c @@ -0,0 +1,172 @@ +/* +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 +#include + +#define GL_GLEXT_PROTOTYPES +#include +//#include +//#include + +struct Pixel +{ + uint8_t b, g, r, a; +}; + +struct Inst +{ + LG_RendererFormat format; + struct Pixel * pixels; + unsigned int yBytes; +}; + +static bool lgd_yuv420_create (void ** opaque); +static void lgd_yuv420_destroy (void * opaque); +static bool lgd_yuv420_initialize (void * opaque, const LG_RendererFormat format, SDL_Window * window); +static void lgd_yuv420_deinitialize (void * opaque); +static LG_OutFormat lgd_yuv420_get_out_format (void * opaque); +static unsigned int lgd_yuv420_get_frame_pitch (void * opaque); +static unsigned int lgd_yuv420_get_frame_stride(void * opaque); +static bool lgd_yuv420_decode (void * opaque, const uint8_t * src, size_t srcSize); +static const uint8_t * lgd_yuv420_get_buffer (void * opaque); + +static bool lgd_yuv420_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_yuv420_destroy(void * opaque) +{ + free(opaque); +} + +static bool lgd_yuv420_initialize(void * opaque, const LG_RendererFormat format, SDL_Window * window) +{ + struct Inst * this = (struct Inst *)opaque; + memcpy(&this->format, &format, sizeof(LG_RendererFormat)); + + this->yBytes = format.width * format.height; + this->pixels = malloc(sizeof(struct Pixel) * (format.width * format.height)); + return true; +} + +static void lgd_yuv420_deinitialize(void * opaque) +{ + struct Inst * this = (struct Inst *)opaque; + free(this->pixels); +} + +static LG_OutFormat lgd_yuv420_get_out_format(void * opaque) +{ + return LG_OUTPUT_BGRA; +} + +static unsigned int lgd_yuv420_get_frame_pitch(void * opaque) +{ + struct Inst * this = (struct Inst *)opaque; + return this->format.width * 4; +} + +static unsigned int lgd_yuv420_get_frame_stride(void * opaque) +{ + struct Inst * this = (struct Inst *)opaque; + return this->format.width; +} + +static bool lgd_yuv420_decode(void * opaque, const uint8_t * src, size_t srcSize) +{ + //FIXME: implement this properly using GLSL + + struct Inst * this = (struct Inst *)opaque; + const unsigned int hw = this->format.width / 2; + const unsigned int hp = this->yBytes / 4; + + for(size_t y = 0; y < this->format.height; ++y) + for(size_t x = 0; x < this->format.width; ++x) + { + const unsigned int yoff = y * this->format.width + x; + const unsigned int uoff = this->yBytes + ((y / 2) * hw + x / 2); + const unsigned int voff = uoff + hp; + + float b = 1.164f * ((float)src[yoff] - 16.0f) + 2.018f * ((float)src[uoff] - 128.0f); + float g = 1.164f * ((float)src[yoff] - 16.0f) - 0.813f * ((float)src[voff] - 128.0f) - 0.391f * ((float)src[uoff] - 128.0f); + float r = 1.164f * ((float)src[yoff] - 16.0f) + 1.596f * ((float)src[voff] - 128.0f); + + #define CLAMP(x) (x < 0 ? 0 : (x > 255 ? 255 : x)) + this->pixels[yoff].b = CLAMP(b); + this->pixels[yoff].g = CLAMP(g); + this->pixels[yoff].r = CLAMP(r); + } + + return true; +} + +static const uint8_t * lgd_yuv420_get_buffer(void * opaque) +{ + struct Inst * this = (struct Inst *)opaque; + return (uint8_t *)this->pixels; +} + +bool lgd_yuv420_init_gl_texture(void * opaque, GLenum target, GLuint texture, void ** ref) +{ + return false; +} + +void lgd_yuv420_free_gl_texture(void * opaque, void * ref) +{ +} + +bool lgd_yuv420_update_gl_texture(void * opaque, void * ref) +{ + return false; +} + +const LG_Decoder LGD_YUV420 = +{ + .name = "YUV420", + .create = lgd_yuv420_create, + .destroy = lgd_yuv420_destroy, + .initialize = lgd_yuv420_initialize, + .deinitialize = lgd_yuv420_deinitialize, + .get_out_format = lgd_yuv420_get_out_format, + .get_frame_pitch = lgd_yuv420_get_frame_pitch, + .get_frame_stride = lgd_yuv420_get_frame_stride, + .decode = lgd_yuv420_decode, + .get_buffer = lgd_yuv420_get_buffer, + + .has_gl = false, //FIXME: Implement this + .init_gl_texture = lgd_yuv420_init_gl_texture, + .free_gl_texture = lgd_yuv420_free_gl_texture, + .update_gl_texture = lgd_yuv420_update_gl_texture +}; \ No newline at end of file diff --git a/client/lg-decoders.h b/client/lg-decoders.h index 7835c361..86155686 100644 --- a/client/lg-decoders.h +++ b/client/lg-decoders.h @@ -21,10 +21,12 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "lg-decoder.h" extern const LG_Decoder LGD_NULL; +extern const LG_Decoder LGD_YUV420; const LG_Decoder * LG_Decoders[] = { &LGD_NULL, + &LGD_YUV420, NULL // end of array sentinal }; diff --git a/client/lg-renderer.h b/client/lg-renderer.h index 1cda88ba..c940e017 100644 --- a/client/lg-renderer.h +++ b/client/lg-renderer.h @@ -24,6 +24,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include +#include "KVMFR.h" + #define IS_LG_RENDERER_VALID(x) \ ((x)->get_name && \ (x)->create && \ @@ -65,21 +67,14 @@ typedef struct LG_RendererParams } LG_RendererParams; -typedef enum LG_RendererComp -{ - LG_COMPRESSION_NONE, - LG_COMPRESSION_H264 -} -LG_RendererComp; - typedef struct LG_RendererFormat { - LG_RendererComp comp; // compression format - unsigned int width; // image width - unsigned int height; // image height - unsigned int stride; // scanline width (zero if compresed) - unsigned int pitch; // scanline bytes (or compressed size) - unsigned int bpp; // bits per pixel (zero if compressed) + FrameType type; // frame type + unsigned int width; // image width + unsigned int height; // image height + unsigned int stride; // scanline width (zero if compresed) + unsigned int pitch; // scanline bytes (or compressed size) + unsigned int bpp; // bits per pixel (zero if compressed) } LG_RendererFormat; diff --git a/client/main.c b/client/main.c index 84ea3c2b..19d4eaeb 100644 --- a/client/main.c +++ b/client/main.c @@ -343,6 +343,7 @@ int frameThread(void * unused) // setup the renderer format with the frame format details LG_RendererFormat lgrFormat; + lgrFormat.type = header.type; lgrFormat.width = header.width; lgrFormat.height = header.height; lgrFormat.stride = header.stride; @@ -353,14 +354,18 @@ int frameThread(void * unused) { case FRAME_TYPE_ARGB: dataSize = lgrFormat.height * lgrFormat.pitch; - lgrFormat.comp = LG_COMPRESSION_NONE; lgrFormat.bpp = 32; break; + case FRAME_TYPE_YUV420: + dataSize = lgrFormat.height * lgrFormat.width; + dataSize += (dataSize / 4) * 2; + lgrFormat.bpp = 12; + break; + case FRAME_TYPE_H264: - dataSize = lgrFormat.pitch; - lgrFormat.comp = LG_COMPRESSION_H264; - lgrFormat.bpp = 0; + dataSize = lgrFormat.pitch; + lgrFormat.bpp = 0; break; default: diff --git a/client/renderers/opengl.c b/client/renderers/opengl.c index ab55d14f..bcccdfe0 100644 --- a/client/renderers/opengl.c +++ b/client/renderers/opengl.c @@ -315,7 +315,7 @@ bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const } if (!this->configured || - this->format.comp != format.comp || + this->format.type != format.type || this->format.width != format.width || this->format.height != format.height || this->format.stride != format.stride || @@ -885,12 +885,16 @@ static bool configure(struct Inst * this, SDL_Window *window) if (this->configured) deconfigure(this); - switch(this->format.comp) + switch(this->format.type) { - case LG_COMPRESSION_NONE: + case FRAME_TYPE_ARGB: this->decoder = &LGD_NULL; break; + case FRAME_TYPE_YUV420: + this->decoder = &LGD_YUV420; + break; + default: DEBUG_ERROR("Unknown/unsupported compression type"); return false;