From cf45b803346682c6f9ad0043e25f565e378441f1 Mon Sep 17 00:00:00 2001 From: max_10 Date: Fri, 21 Sep 2018 19:00:11 +0200 Subject: [PATCH] generic-pc: rework glfb (thx seife) --- generic-pc/glfb.cpp | 179 +++++++++++++++++------------ generic-pc/{glfb.h => glfb_priv.h} | 38 +++--- generic-pc/init.cpp | 1 + generic-pc/video.cpp | 33 ++++-- generic-pc/video_lib.h | 4 +- include/glfb.h | 50 ++++++-- 6 files changed, 191 insertions(+), 114 deletions(-) rename generic-pc/{glfb.h => glfb_priv.h} (82%) diff --git a/generic-pc/glfb.cpp b/generic-pc/glfb.cpp index 3811056..19d1f29 100644 --- a/generic-pc/glfb.cpp +++ b/generic-pc/glfb.cpp @@ -38,8 +38,7 @@ #include #include #include -#include "glfb.h" -#include +#include "glfb_priv.h" #include "video_lib.h" #include "audio_lib.h" @@ -54,10 +53,36 @@ extern cVideo *videoDecoder; extern cAudio *audioDecoder; -static GLFramebuffer *gThiz = 0; /* GLUT does not allow for an arbitrary argument to the render func */ +/* the private class that does stuff only needed inside libstb-hal. + * is used e.g. by cVideo... */ +GLFbPC *glfb_priv = NULL; -GLFramebuffer::GLFramebuffer(int x, int y): mReInit(true), mShutDown(false), mInitDone(false) +GLFramebuffer::GLFramebuffer(int x, int y) { + Init(); + glfb_priv = new GLFbPC(x, y, osd_buf); + si = glfb_priv->getScreenInfo(); + start(); + while (!glfb_priv->mInitDone) + usleep(1); +} + +GLFramebuffer::~GLFramebuffer() +{ + glfb_priv->mShutDown = true; + join(); + delete glfb_priv; + glfb_priv = NULL; +} + +void GLFramebuffer::blit() +{ + glfb_priv->blit(); +} + +GLFbPC::GLFbPC(int x, int y, std::vector &buf): mReInit(true), mShutDown(false), mInitDone(false) +{ + osd_buf = &buf; mState.width = x; mState.height = y; mX = &_mX[0]; @@ -78,19 +103,19 @@ GLFramebuffer::GLFramebuffer(int x, int y): mReInit(true), mShutDown(false), mIn last_apts = 0; /* linux framebuffer compat mode */ - screeninfo.bits_per_pixel = 32; - screeninfo.xres = mState.width; - screeninfo.xres_virtual = screeninfo.xres; - screeninfo.yres = mState.height; - screeninfo.yres_virtual = screeninfo.yres; - screeninfo.blue.length = 8; - screeninfo.blue.offset = 0; - screeninfo.green.length = 8; - screeninfo.green.offset = 8; - screeninfo.red.length = 8; - screeninfo.red.offset = 16; - screeninfo.transp.length = 8; - screeninfo.transp.offset = 24; + si.bits_per_pixel = 32; + si.xres = mState.width; + si.xres_virtual = si.xres; + si.yres = mState.height; + si.yres_virtual = si.yres; + si.blue.length = 8; + si.blue.offset = 0; + si.green.length = 8; + si.green.offset = 8; + si.red.length = 8; + si.red.offset = 16; + si.transp.length = 8; + si.transp.offset = 24; unlink("/tmp/neutrino.input"); mkfifo("/tmp/neutrino.input", 0600); @@ -98,21 +123,22 @@ GLFramebuffer::GLFramebuffer(int x, int y): mReInit(true), mShutDown(false), mIn if (input_fd < 0) lt_info("%s: could not open /tmp/neutrino.input FIFO: %m\n", __func__); initKeys(); - OpenThreads::Thread::start(); - while (!mInitDone) - usleep(1); } -GLFramebuffer::~GLFramebuffer() +GLFbPC::~GLFbPC() { mShutDown = true; - OpenThreads::Thread::join(); if (input_fd >= 0) close(input_fd); + osd_buf->clear(); } -void GLFramebuffer::initKeys() +void GLFbPC::initKeys() { + /* + Keep in sync with initKeys() in clutterfb.cpp + */ + mSpecialMap[GLUT_KEY_UP] = KEY_UP; mSpecialMap[GLUT_KEY_DOWN] = KEY_DOWN; mSpecialMap[GLUT_KEY_LEFT] = KEY_LEFT; @@ -133,8 +159,8 @@ void GLFramebuffer::initKeys() mSpecialMap[GLUT_KEY_F11] = KEY_NEXT; mSpecialMap[GLUT_KEY_F12] = KEY_PREVIOUS; - mSpecialMap[GLUT_KEY_PAGE_UP] = KEY_PAGEUP; - mSpecialMap[GLUT_KEY_PAGE_DOWN] = KEY_PAGEDOWN; + mSpecialMap[GLUT_KEY_PAGE_UP] = KEY_PAGEUP; + mSpecialMap[GLUT_KEY_PAGE_DOWN] = KEY_PAGEDOWN; mKeyMap[0x0d] = KEY_OK; mKeyMap[0x1b] = KEY_EXIT; @@ -177,9 +203,21 @@ void GLFramebuffer::initKeys() void GLFramebuffer::run() { - setupCtx(); - setupOSDBuffer(); - mInitDone = true; /* signal that setup is finished */ + int argc = 1; + int x = glfb_priv->mState.width; + int y = glfb_priv->mState.height; + /* some dummy commandline for GLUT to be happy */ + char const *argv[2] = { "neutrino", 0 }; + lt_info("GLFB: GL thread starting x %d y %d\n", x, y); + glutInit(&argc, const_cast(argv)); + glutInitWindowSize(x, y); + glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); + glutCreateWindow("Neutrino"); + /* 32bit FB depth, *2 because tuxtxt uses a shadow buffer */ + int fbmem = x * y * 4 * 2; + osd_buf.resize(fbmem); + lt_info("GLFB: OSD buffer set to %d bytes at 0x%p\n", fbmem, osd_buf.data()); + glfb_priv->mInitDone = true; /* signal that setup is finished */ /* init the good stuff */ GLenum err = glewInit(); @@ -194,16 +232,15 @@ void GLFramebuffer::run() } else { - gThiz = this; glutSetCursor(GLUT_CURSOR_NONE); - glutDisplayFunc(GLFramebuffer::rendercb); - glutKeyboardFunc(GLFramebuffer::keyboardcb); - glutSpecialFunc(GLFramebuffer::specialcb); - glutReshapeFunc(GLFramebuffer::resizecb); - setupGLObjects(); /* needs GLEW prototypes */ + glutDisplayFunc(GLFbPC::rendercb); + glutKeyboardFunc(GLFbPC::keyboardcb); + glutSpecialFunc(GLFbPC::specialcb); + glutReshapeFunc(GLFbPC::resizecb); + glfb_priv->setupGLObjects(); /* needs GLEW prototypes */ glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); glutMainLoop(); - releaseGLObjects(); + glfb_priv->releaseGLObjects(); } } else @@ -211,21 +248,20 @@ void GLFramebuffer::run() lt_info("GLFB: GL thread stopping\n"); } - -void GLFramebuffer::setupCtx() +#if 0 +void GLFbPC::setupCtx() { int argc = 1; /* some dummy commandline for GLUT to be happy */ char const *argv[2] = { "neutrino", 0 }; - lt_info("GLFB: GL thread starting\n"); + lt_info("GLFB: GL thread starting x %d y %d\n", mX[0], mY[0]); glutInit(&argc, const_cast(argv)); glutInitWindowSize(mX[0], mY[0]); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutCreateWindow("Neutrino"); - GLWinID = glXGetCurrentDrawable(); // this was the holy grail to get the right window handle for gstreamer :D } -void GLFramebuffer::setupOSDBuffer() +void GLFbPC::setupOSDBuffer() { /* the OSD buffer size can be decoupled from the actual window size since the GL can blit-stretch with no trouble at all, ah, the luxury of ignorance... */ @@ -234,12 +270,13 @@ void GLFramebuffer::setupOSDBuffer() { /* 32bit FB depth, *2 because tuxtxt uses a shadow buffer */ int fbmem = mState.width * mState.height * 4 * 2; - mOSDBuffer.resize(fbmem); - lt_info("GLFB: OSD buffer set to %d bytes\n", fbmem); + osd_buf->resize(fbmem); + lt_info("GLFB: OSD buffer set to %d bytes at 0x%p\n", fbmem, osd_buf->data()); } } +#endif -void GLFramebuffer::setupGLObjects() +void GLFbPC::setupGLObjects() { unsigned char buf[4] = { 0, 0, 0, 0 }; /* 1 black pixel */ glGenTextures(1, &mState.osdtex); @@ -267,7 +304,7 @@ void GLFramebuffer::setupGLObjects() } -void GLFramebuffer::releaseGLObjects() +void GLFbPC::releaseGLObjects() { glDeleteBuffers(1, &mState.pbo); glDeleteBuffers(1, &mState.displaypbo); @@ -276,56 +313,56 @@ void GLFramebuffer::releaseGLObjects() } -/* static */ void GLFramebuffer::rendercb() +/* static */ void GLFbPC::rendercb() { - gThiz->render(); + glfb_priv->render(); } -/* static */ void GLFramebuffer::keyboardcb(unsigned char key, int /*x*/, int /*y*/) +/* static */ void GLFbPC::keyboardcb(unsigned char key, int /*x*/, int /*y*/) { lt_debug_c("GLFB::%s: 0x%x\n", __func__, key); struct input_event ev; if (key == 'f') { - lt_info_c("GLFB::%s: toggle fullscreen %s\n", __func__, gThiz->mFullscreen?"off":"on"); - gThiz->mFullscreen = !(gThiz->mFullscreen); - gThiz->mReInit = true; + lt_info_c("GLFB::%s: toggle fullscreen %s\n", __func__, glfb_priv->mFullscreen?"off":"on"); + glfb_priv->mFullscreen = !(glfb_priv->mFullscreen); + glfb_priv->mReInit = true; return; } - std::map::const_iterator i = gThiz->mKeyMap.find(key); - if (i == gThiz->mKeyMap.end()) + std::map::const_iterator i = glfb_priv->mKeyMap.find(key); + if (i == glfb_priv->mKeyMap.end()) return; ev.code = i->second; ev.value = 1; /* key own */ ev.type = EV_KEY; gettimeofday(&ev.time, NULL); lt_debug_c("GLFB::%s: pushing 0x%x\n", __func__, ev.code); - write(gThiz->input_fd, &ev, sizeof(ev)); + write(glfb_priv->input_fd, &ev, sizeof(ev)); ev.value = 0; /* neutrino is stupid, so push key up directly after key down */ - write(gThiz->input_fd, &ev, sizeof(ev)); + write(glfb_priv->input_fd, &ev, sizeof(ev)); } -/* static */ void GLFramebuffer::specialcb(int key, int /*x*/, int /*y*/) +/* static */ void GLFbPC::specialcb(int key, int /*x*/, int /*y*/) { lt_debug_c("GLFB::%s: 0x%x\n", __func__, key); struct input_event ev; - std::map::const_iterator i = gThiz->mSpecialMap.find(key); - if (i == gThiz->mSpecialMap.end()) + std::map::const_iterator i = glfb_priv->mSpecialMap.find(key); + if (i == glfb_priv->mSpecialMap.end()) return; ev.code = i->second; ev.value = 1; ev.type = EV_KEY; gettimeofday(&ev.time, NULL); lt_debug_c("GLFB::%s: pushing 0x%x\n", __func__, ev.code); - write(gThiz->input_fd, &ev, sizeof(ev)); + write(glfb_priv->input_fd, &ev, sizeof(ev)); ev.value = 0; - write(gThiz->input_fd, &ev, sizeof(ev)); + write(glfb_priv->input_fd, &ev, sizeof(ev)); } int sleep_us = 30000; -void GLFramebuffer::render() +void GLFbPC::render() { if(mShutDown) glutLeaveMainLoop(); @@ -453,12 +490,12 @@ void GLFramebuffer::render() glutPostRedisplay(); } -/* static */ void GLFramebuffer::resizecb(int w, int h) +/* static */ void GLFbPC::resizecb(int w, int h) { - gThiz->checkReinit(w, h); + glfb_priv->checkReinit(w, h); } -void GLFramebuffer::checkReinit(int x, int y) +void GLFbPC::checkReinit(int x, int y) { static int last_x = 0, last_y = 0; @@ -478,7 +515,7 @@ void GLFramebuffer::checkReinit(int x, int y) last_y = y; } -void GLFramebuffer::drawSquare(float size, float x_factor) +void GLFbPC::drawSquare(float size, float x_factor) { GLfloat vertices[] = { 1.0f, 1.0f, @@ -534,11 +571,11 @@ void GLFramebuffer::drawSquare(float size, float x_factor) } -void GLFramebuffer::bltOSDBuffer() +void GLFbPC::bltOSDBuffer() { /* FIXME: copy each time */ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mState.pbo); - glBufferData(GL_PIXEL_UNPACK_BUFFER, mOSDBuffer.size(), &mOSDBuffer[0], GL_STREAM_DRAW_ARB); + glBufferData(GL_PIXEL_UNPACK_BUFFER, osd_buf->size(), osd_buf->data(), GL_STREAM_DRAW_ARB); glBindTexture(GL_TEXTURE_2D, mState.osdtex); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mState.width, mState.height, GL_BGRA, GL_UNSIGNED_BYTE, 0); @@ -546,7 +583,7 @@ void GLFramebuffer::bltOSDBuffer() glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } -void GLFramebuffer::bltDisplayBuffer() +void GLFbPC::bltDisplayBuffer() { if (!videoDecoder) /* cannot start yet */ return; @@ -554,7 +591,7 @@ void GLFramebuffer::bltDisplayBuffer() cVideo::SWFramebuffer *buf = videoDecoder->getDecBuf(); if (!buf) { if (warn) - lt_debug("GLFB::%s did not get a buffer...\n", __func__); + lt_info("GLFB::%s did not get a buffer...\n", __func__); warn = false; return; } @@ -607,9 +644,3 @@ void GLFramebuffer::bltDisplayBuffer() lt_debug("vpts: 0x%" PRIx64 " apts: 0x%" PRIx64 " diff: %6.3f sleep_us %d buf %d\n", buf->pts(), apts, (buf->pts() - apts)/90000.0, sleep_us, videoDecoder->buf_num); } - -void GLFramebuffer::clear() -{ - /* clears front and back buffer */ - memset(&mOSDBuffer[0], 0, mOSDBuffer.size()); -} diff --git a/generic-pc/glfb.h b/generic-pc/glfb_priv.h similarity index 82% rename from generic-pc/glfb.h rename to generic-pc/glfb_priv.h index bc7b9f5..a51545a 100644 --- a/generic-pc/glfb.h +++ b/generic-pc/glfb_priv.h @@ -14,12 +14,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . + + ******************************************************************** + private stuff of the GLFB thread that is only used inside libstb-hal + and not exposed to the application. */ -#ifndef __glthread__ -#define __glthread__ -#include -#include +#ifndef __glfb_priv__ +#define __glfb_priv__ #include #include #include @@ -32,28 +34,26 @@ #if USE_CLUTTER #include #endif +#include "glfb.h" extern "C" { #include } -class GLFramebuffer : public OpenThreads::Thread +class GLFbPC { public: - GLFramebuffer(int x, int y); - ~GLFramebuffer(); - void run(); - std::vector *getOSDBuffer() { return &mOSDBuffer; } /* pointer to OSD bounce buffer */ + GLFbPC(int x, int y, std::vector &buf); + ~GLFbPC(); + std::vector *getOSDBuffer() { return osd_buf; } /* pointer to OSD bounce buffer */ int getOSDWidth() { return mState.width; } int getOSDHeight() { return mState.height; } - void blit() { mState.blit = true; } - fb_var_screeninfo getScreenInfo() { return screeninfo; } + void blit() { mState.blit = true; }; + fb_var_screeninfo getScreenInfo() { return si; } void setOutputFormat(AVRational a, int h, int c) { mOA = a; *mY = h; mCrop = c; mReInit = true; } - - void clear(); - int getWindowID() { return GLWinID; } - +/* just make everything public for simplicity - this is only used inside libstb-hal anyway private: - fb_var_screeninfo screeninfo; +*/ + fb_var_screeninfo si; int *mX; int *mY; int _mX[2]; /* output window size */ @@ -65,7 +65,6 @@ private: float zoom; /* for cropping */ float xscale; /* and aspect ratio */ int mCrop; /* DISPLAY_AR_MODE */ - int GLWinID; bool mFullscreen; /* fullscreen? */ bool mReInit; /* setup things for GL */ @@ -75,7 +74,7 @@ private: // OpenThreads::Condition mInitCond; /* condition variable for init */ // mutable OpenThreads::Mutex mMutex; /* lock our data */ - std::vector mOSDBuffer; /* silly bounce buffer */ + std::vector *osd_buf; /* silly bounce buffer */ #if USE_OPENGL std::map mKeyMap; @@ -86,6 +85,7 @@ private: #endif int input_fd; int64_t last_apts; + void run(); static void rendercb(); /* callback for GLUT */ void render(); /* actual render function */ @@ -103,8 +103,10 @@ private: #endif void initKeys(); /* setup key bindings for window */ +#if 0 void setupCtx(); /* create the window and make the context current */ void setupOSDBuffer(); /* create the OSD buffer */ +#endif struct { int width; /* width and height, fixed for a framebuffer instance */ diff --git a/generic-pc/init.cpp b/generic-pc/init.cpp index b641fd8..fa2a01c 100644 --- a/generic-pc/init.cpp +++ b/generic-pc/init.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "init_td.h" diff --git a/generic-pc/video.cpp b/generic-pc/video.cpp index 8237305..be89bf2 100644 --- a/generic-pc/video.cpp +++ b/generic-pc/video.cpp @@ -49,7 +49,7 @@ extern "C" { #include "video_lib.h" #include "dmx_hal.h" -#include "glfb.h" +#include "glfb_priv.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args) @@ -57,7 +57,7 @@ extern "C" { cVideo *videoDecoder = NULL; extern cDemux *videoDemux; -extern GLFramebuffer *glfb; +extern GLFbPC *glfb_priv; int system_rev = 0; extern bool HAL_nodec; @@ -111,7 +111,7 @@ int cVideo::setAspectRatio(int vformat, int cropping) if (cropping >= 0) display_crop = (DISPLAY_AR_MODE) cropping; if (display_aspect < DISPLAY_AR_RAW && output_h > 0) /* don't know what to do with this */ - glfb->setOutputFormat(aspect_ratios[display_aspect], output_h, display_crop); + glfb_priv->setOutputFormat(aspect_ratios[display_aspect], output_h, display_crop); return 0; } @@ -222,7 +222,7 @@ int cVideo::SetVideoSystem(int system, bool) // v_std = (VIDEO_STD) system; output_h = h; if (display_aspect < DISPLAY_AR_RAW && output_h > 0) /* don't know what to do with this */ - glfb->setOutputFormat(aspect_ratios[display_aspect], output_h, display_crop); + glfb_priv->setOutputFormat(aspect_ratios[display_aspect], output_h, display_crop); return 0; } @@ -369,6 +369,7 @@ void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/, int / pig_y = y; pig_w = w; pig_h = h; + pig_changed = true; } void cVideo::getPictureInfo(int &width, int &height, int &rate) @@ -572,10 +573,18 @@ void cVideo::run(void) f->width(c->width); f->height(c->height); int64_t vpts = av_frame_get_best_effort_timestamp(frame); + /* a/v delay determined experimentally :-) */ +#if USE_OPENGL if (v_format == VIDEO_FORMAT_MPEG2) vpts += 90000*4/10; /* 400ms */ else vpts += 90000*3/10; /* 300ms */ +#endif +#if USE_CLUTTER + /* no idea why there's a difference between OpenGL and clutter rendering... */ + if (v_format == VIDEO_FORMAT_MPEG2) + vpts += 90000*3/10; /* 300ms */ +#endif f->pts(vpts); AVRational a = av_guess_sample_aspect_ratio(avfc, avfc->streams[0], frame); f->AR(a); @@ -675,8 +684,8 @@ bool cVideo::GetScreenImage(unsigned char * &data, int &xres, int &yres, bool ge std::vector *osd = NULL; std::vector s_osd; /* scaled OSD */ int vid_w = 0, vid_h = 0; - int osd_w = glfb->getOSDWidth(); - int osd_h = glfb->getOSDHeight(); + int osd_w = glfb_priv->getOSDWidth(); + int osd_h = glfb_priv->getOSDHeight(); xres = osd_w; yres = osd_h; if (get_video) { @@ -700,24 +709,26 @@ bool cVideo::GetScreenImage(unsigned char * &data, int &xres, int &yres, bool ge yres = osd_h; } if (get_osd) - osd = glfb->getOSDBuffer(); + osd = glfb_priv->getOSDBuffer(); unsigned int need = av_image_get_buffer_size(AV_PIX_FMT_RGB32, xres, yres, 1); data = (unsigned char *)realloc(data, need); /* will be freed by caller */ if (data == NULL) /* out of memory? */ return false; if (get_video) { - //memcpy dont work with copy BGR24 to RGB32 +#if USE_OPENGL //memcpy dont work with copy BGR24 to RGB32 if (vid_w != xres || vid_h != yres){ /* scale video into data... */ - bool ret = swscale(&video[0], data, vid_w, vid_h, xres, yres, AV_PIX_FMT_RGB32); +#endif + bool ret = swscale(&video[0], data, vid_w, vid_h, xres, yres,VDEC_PIXFMT); if(!ret){ free(data); return false; } - //memcpy dont work with copy BGR24 to RGB32 - } else { /* get_video and no fancy scaling needed */ +#if USE_OPENGL //memcpy dont work with copy BGR24 to RGB32 + }else{ /* get_video and no fancy scaling needed */ memcpy(data, &video[0], xres * yres * sizeof(uint32_t)); } +#endif } if (get_osd && (osd_w != xres || osd_h != yres)) { diff --git a/generic-pc/video_lib.h b/generic-pc/video_lib.h index 694965c..1bec147 100644 --- a/generic-pc/video_lib.h +++ b/generic-pc/video_lib.h @@ -120,10 +120,10 @@ typedef enum } VIDEO_CONTROL; -#define VDEC_MAXBUFS 0x30 +#define VDEC_MAXBUFS 0x40 class cVideo : public OpenThreads::Thread { - friend class GLFramebuffer; + friend class GLFbPC; friend class cDemux; private: /* called from GL thread */ diff --git a/include/glfb.h b/include/glfb.h index ea7addd..5b95ce6 100644 --- a/include/glfb.h +++ b/include/glfb.h @@ -1,10 +1,42 @@ -#include -#if HAVE_GENERIC_HARDWARE -#if BOXMODEL_RASPI -#include "../raspi/glfb.h" -#else -#include "../generic-pc/glfb.h" -#endif -#else -#error glfb.h only works with HAVE_GENERIC_HARDWARE defined +/* + Copyright 2010 Carsten Juttner + Copyright 2012,2013 Stefan Seyfried + + 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, see . +*/ + +#ifndef __glfb__ +#define __glfb__ +#include +#include +#include /* for screeninfo etc. */ + +class GLFramebuffer : public OpenThreads::Thread +{ +public: + GLFramebuffer(int x, int y); + ~GLFramebuffer(); + std::vector *getOSDBuffer() { return &osd_buf; } /* pointer to OSD bounce buffer */ + void blit(); + fb_var_screeninfo getScreenInfo() { return si; } + +private: + fb_var_screeninfo si; + std::vector osd_buf; /* silly bounce buffer */ + void run(); /* for OpenThreads::Thread */ + void setup(); + void blit_osd(); + void *pdata; /* not yet used */ +}; #endif