diff --git a/configure.ac b/configure.ac index cac007d..aad1437 100644 --- a/configure.ac +++ b/configure.ac @@ -20,6 +20,18 @@ AC_DISABLE_STATIC AC_SYS_LARGEFILE AC_PROG_LIBTOOL +AC_ARG_ENABLE(clutter, + AS_HELP_STRING(--enable-clutter, use clutter instead of OpenGL), + ,[enable_clutter=no]) + +AM_CONDITIONAL(USE_CLUTTER,test "$enable_clutter" = "yes") +AM_CONDITIONAL(USE_OPENGL,test "$enable_clutter" = "no") +if test "$enable_clutter" = "yes"; then + AC_DEFINE(USE_CLUTTER,1,[use clutter instead of opengl]) +else + AC_DEFINE(USE_OPENGL,1,[use opengl instead of clutter]) +fi + if test x"$BOXTYPE" = x"tripledragon"; then PKG_CHECK_MODULES([DIRECTFB], [directfb]) fi @@ -54,6 +66,9 @@ if test x$BOXTYPE = xarmbox -a "$enable_gstreamer_10" = "yes"; then fi if test x$BOXTYPE = xgeneric -a x$BOXMODEL != xraspi; then + if test x"$enable_clutter" = xyes; then + PKG_CHECK_MODULES([CLUTTER], [clutter-1.0]) + fi PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 53.21.1]) PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0]) # don't know which version is exactly needed here... diff --git a/generic-pc/Makefile.am b/generic-pc/Makefile.am index fd230bd..e2b6a5e 100644 --- a/generic-pc/Makefile.am +++ b/generic-pc/Makefile.am @@ -1,30 +1,44 @@ noinst_LTLIBRARIES = libgeneric.la AM_CPPFLAGS = -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS +AM_CPPFLAGS += -Wfatal-errors AM_CPPFLAGS += \ -I$(top_srcdir)/common \ - -I$(top_srcdir)/include + -I$(top_srcdir)/include \ + @AVUTIL_CFLAGS@ \ + @CLUTTER_CFLAGS@ AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing AM_LDFLAGS = \ - -lglut -lGL -lGLU -lGLEW -lao \ + -lao \ -lOpenThreads \ @AVFORMAT_LIBS@ \ @AVUTIL_LIBS@ \ @AVCODEC_LIBS@ \ @SWRESAMPLE_LIBS@ \ - @SWSCALE_LIBS@ + @SWSCALE_LIBS@ \ + @CLUTTER_LIBS@ + +if USE_OPENGL +AM_LDFLAGS += -lglut -lGL -lGLU -lGLEW -lao +endif libgeneric_la_SOURCES = \ hardware_caps.c \ dmx.cpp \ video.cpp \ audio.cpp \ - glfb.cpp \ init.cpp \ record.cpp +if USE_CLUTTER +libgeneric_la_SOURCES += clutterfb.cpp +endif +if USE_OPENGL +libgeneric_la_SOURCES += glfb.cpp +endif + if ENABLE_GSTREAMER_01 libgeneric_la_SOURCES += \ playback_gst_01.cpp diff --git a/generic-pc/clutterfb.cpp b/generic-pc/clutterfb.cpp new file mode 100644 index 0000000..2c61def --- /dev/null +++ b/generic-pc/clutterfb.cpp @@ -0,0 +1,481 @@ +/* + Framebuffer implementation using clutter https://developer.gnome.org/clutter/ + Copyright (C) 2016 Stefan Seyfried + + based on the openGL framebuffer implementation + 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 . + + TODO: AV-Sync code is "experimental" at best +*/ + +#include "config.h" +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "glfb_priv.h" +#include "video_priv.h" +#include "audio_priv.h" + +#include + +#include "lt_debug.h" + +#define lt_debug_c(args...) _lt_debug(HAL_DEBUG_INIT, NULL, args) +#define lt_info_c(args...) _lt_info(HAL_DEBUG_INIT, NULL, args) +#define lt_debug(args...) _lt_debug(HAL_DEBUG_INIT, this, args) +#define lt_info(args...) _lt_info(HAL_DEBUG_INIT, this, args) + + +extern VDec *vdec; +extern ADec *adec; + +/* 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) +{ + 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]; + mY = &_mY[0]; + *mX = x; + *mY = y; + av_reduce(&mOA.num, &mOA.den, x, y, INT_MAX); + mVA = mOA; /* initial aspect ratios are from the FB resolution, those */ + _mVA = mVA; /* will be updated by the videoDecoder functions anyway */ + mVAchanged = true; + mCrop = DISPLAY_AR_MODE_PANSCAN; + zoom = 1.0; + xscale = 1.0; + const char *tmp = getenv("GLFB_FULLSCREEN"); + mFullscreen = !!(tmp); + + mState.blit = true; + last_apts = 0; + + /* linux framebuffer compat mode */ + 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); + input_fd = open("/tmp/neutrino.input", O_RDWR|O_CLOEXEC|O_NONBLOCK); + if (input_fd < 0) + lt_info("%s: could not open /tmp/neutrino.input FIFO: %m\n", __func__); + initKeys(); +} + +GLFbPC::~GLFbPC() +{ + mShutDown = true; + if (input_fd >= 0) + close(input_fd); + osd_buf->clear(); +} + +void GLFbPC::initKeys() +{ + mKeyMap[CLUTTER_KEY_Up] = KEY_UP; + mKeyMap[CLUTTER_KEY_Down] = KEY_DOWN; + mKeyMap[CLUTTER_KEY_Left] = KEY_LEFT; + mKeyMap[CLUTTER_KEY_Right] = KEY_RIGHT; + + mKeyMap[CLUTTER_KEY_F1] = KEY_RED; + mKeyMap[CLUTTER_KEY_F2] = KEY_GREEN; + mKeyMap[CLUTTER_KEY_F3] = KEY_YELLOW; + mKeyMap[CLUTTER_KEY_F4] = KEY_BLUE; + + mKeyMap[CLUTTER_KEY_F5] = KEY_WWW; + mKeyMap[CLUTTER_KEY_F6] = KEY_SUBTITLE; + mKeyMap[CLUTTER_KEY_F7] = KEY_MOVE; + mKeyMap[CLUTTER_KEY_F8] = KEY_SLEEP; + + mKeyMap[CLUTTER_KEY_Page_Up] = KEY_PAGEUP; + mKeyMap[CLUTTER_KEY_Page_Down] = KEY_PAGEDOWN; + + mKeyMap[CLUTTER_KEY_Return] = KEY_OK; + mKeyMap[CLUTTER_KEY_Escape] = KEY_EXIT; + mKeyMap['e'] = KEY_EPG; + mKeyMap['i'] = KEY_INFO; + mKeyMap['m'] = KEY_MENU; + + mKeyMap['+'] = KEY_VOLUMEUP; + mKeyMap['-'] = KEY_VOLUMEDOWN; + mKeyMap['.'] = KEY_MUTE; + mKeyMap['h'] = KEY_HELP; + mKeyMap['p'] = KEY_POWER; + + mKeyMap['0'] = KEY_0; + mKeyMap['1'] = KEY_1; + mKeyMap['2'] = KEY_2; + mKeyMap['3'] = KEY_3; + mKeyMap['4'] = KEY_4; + mKeyMap['5'] = KEY_5; + mKeyMap['6'] = KEY_6; + mKeyMap['7'] = KEY_7; + mKeyMap['8'] = KEY_8; + mKeyMap['9'] = KEY_9; +} + +static ClutterActor *stage = NULL; +static ClutterActor *fb_actor = NULL; +static ClutterActor *vid_actor = NULL; +static ClutterTimeline *tl = NULL; +void GLFramebuffer::run() +{ + int argc = 1; + int x = glfb_priv->mState.width; + int y = glfb_priv->mState.height; + /* some dummy commandline for GLUT to be happy */ + char *a = (char *)"neutrino"; + char **argv = (char **)malloc(sizeof(char *) * 2); + argv[0] = a; + argv[1] = NULL; + lt_info("GLFB: GL thread starting x %d y %d\n", x, y); + if (clutter_init(&argc, &argv) != CLUTTER_INIT_SUCCESS) { + lt_info("GLFB: error initializing clutter\n"); + return; + } + lt_info("GLFB: %s:%d\n", __func__, __LINE__); + ClutterColor stage_color = { 0, 0, 0, 255 }; + stage = clutter_stage_new(); + clutter_actor_set_size(stage, x, y); + clutter_actor_set_background_color(stage, &stage_color); + clutter_actor_set_content_gravity(stage, CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT); + //g_signal_connect(stage, "destroy", G_CALLBACK(clutter_main_quit), NULL); + g_signal_connect(stage, "key-press-event", G_CALLBACK(GLFbPC::keyboardcb), (void *)1); + g_signal_connect(stage, "key-release-event", G_CALLBACK(GLFbPC::keyboardcb), NULL); + clutter_stage_set_user_resizable(CLUTTER_STAGE (stage), TRUE); + clutter_actor_grab_key_focus(stage); + clutter_actor_show(stage); + + /* 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()); + + /* video plane is below FB plane, so it comes first */ + vid_actor = clutter_actor_new(); + ClutterContent *fb = clutter_image_new(); + /* osd_buf, because it starts up black */ + if (!clutter_image_set_data(CLUTTER_IMAGE(fb), osd_buf.data(), COGL_PIXEL_FORMAT_BGR_888, x, y, x*3, NULL)) { + lt_info("GLFB::%s clutter_image_set_data failed? (vid)\n", __func__); + _exit(1); /* life is hard */ + } + clutter_actor_set_content(vid_actor, fb); + g_object_unref(fb); + clutter_actor_set_size(vid_actor, x, y); + clutter_actor_set_position(vid_actor, 0, 0); + clutter_actor_add_constraint(vid_actor, clutter_bind_constraint_new(stage, CLUTTER_BIND_WIDTH, 0)); + clutter_actor_add_constraint(vid_actor, clutter_bind_constraint_new(stage, CLUTTER_BIND_HEIGHT, 0)); + clutter_actor_add_constraint(vid_actor, clutter_bind_constraint_new(stage, CLUTTER_BIND_X, 0)); + clutter_actor_add_constraint(vid_actor, clutter_bind_constraint_new(stage, CLUTTER_BIND_Y, 0)); + clutter_actor_set_content_gravity(vid_actor, CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT); + clutter_actor_set_pivot_point(vid_actor, 0.5, 0.5); + clutter_actor_add_child(stage, vid_actor); + clutter_actor_show(vid_actor); + + fb_actor = clutter_actor_new(); + fb = clutter_image_new(); + if (!clutter_image_set_data(CLUTTER_IMAGE(fb), osd_buf.data(), COGL_PIXEL_FORMAT_BGRA_8888, x, y, x*4, NULL)) { + lt_info("GLFB::%s clutter_image_set_data failed? (osd)\n", __func__); + _exit(1); /* life is hard */ + } + clutter_actor_set_content(fb_actor, fb); + g_object_unref(fb); + clutter_actor_set_size(fb_actor, x, y); + clutter_actor_set_position(fb_actor, 0, 0); + clutter_actor_add_constraint(fb_actor, clutter_bind_constraint_new(stage, CLUTTER_BIND_WIDTH, 0)); + clutter_actor_add_constraint(fb_actor, clutter_bind_constraint_new(stage, CLUTTER_BIND_HEIGHT, 0)); + clutter_actor_add_constraint(fb_actor, clutter_bind_constraint_new(stage, CLUTTER_BIND_X, 0)); + clutter_actor_add_constraint(fb_actor, clutter_bind_constraint_new(stage, CLUTTER_BIND_Y, 0)); + clutter_actor_set_content_gravity(fb_actor, CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT); + clutter_actor_add_child(stage, fb_actor); + clutter_actor_show(fb_actor); + + glfb_priv->mInitDone = true; /* signal that setup is finished */ + tl = clutter_timeline_new(100); + g_signal_connect(tl, "new-frame", G_CALLBACK(GLFbPC::rendercb), NULL); + clutter_timeline_set_repeat_count(tl, -1); + clutter_timeline_start(tl); + clutter_main(); + lt_info("GLFB: GL thread stopping\n"); +} + +/* static */ void GLFbPC::rendercb() +{ + glfb_priv->render(); +} + +/* static */ bool GLFbPC::keyboardcb(ClutterActor * /*actor*/, ClutterEvent *event, gpointer user_data) +{ + guint key = clutter_event_get_key_symbol (event); + int keystate = user_data ? 1 : 0; + lt_info_c("GLFB::%s: 0x%x, %d\n", __func__, key, keystate); + + struct input_event ev; + if (key == 'f' && keystate) + { + 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 true; + } + std::map::const_iterator i = glfb_priv->mKeyMap.find(key); + if (i == glfb_priv->mKeyMap.end()) + return true; + ev.code = i->second; + ev.value = keystate; /* key own */ + ev.type = EV_KEY; + gettimeofday(&ev.time, NULL); + lt_debug_c("GLFB::%s: pushing 0x%x\n", __func__, ev.code); + write(glfb_priv->input_fd, &ev, sizeof(ev)); + return true; +} + +int sleep_us = 30000; + +void GLFbPC::render() +{ + if(mShutDown) + clutter_main_quit(); + + mReInitLock.lock(); + if (mReInit) + { + int xoff = 0; + int yoff = 0; + mVAchanged = true; + mReInit = false; +#if 0 + mX = &_mX[mFullscreen]; + mY = &_mY[mFullscreen]; +#endif + *mX = *mY * mOA.num / mOA.den; + if (mFullscreen) { + clutter_stage_set_fullscreen(CLUTTER_STAGE(stage), TRUE); + clutter_actor_show(stage); + clutter_stage_ensure_redraw(CLUTTER_STAGE(stage)); + } else { + clutter_stage_set_fullscreen(CLUTTER_STAGE(stage), FALSE); +// *mX = *mY * mOA.num / mOA.den; + clutter_actor_set_size(stage, *mX, *mY); + } + lt_info("%s: reinit mX:%d mY:%d xoff:%d yoff:%d fs %d\n", + __func__, *mX, *mY, xoff, yoff, mFullscreen); + } + mReInitLock.unlock(); + + bltDisplayBuffer(); /* decoded video stream */ + if (mState.blit) { + /* only blit manually after fb->blit(), this helps to find missed blit() calls */ + mState.blit = false; + lt_debug("GLFB::%s blit!\n", __func__); + bltOSDBuffer(); /* OSD */ + } + + if (mVAchanged) + { + mVAchanged = false; + zoom = 1.0; + float xzoom = 1.0; + //xscale = 1.0; + int cmp = av_cmp_q(mVA, mOA); + const AVRational a149 = { 14, 9 }; + switch (cmp) { + default: + case INT_MIN: /* invalid */ + case 0: /* identical */ + lt_debug("%s: mVA == mOA (or fullscreen mode :-)\n", __func__); + break; + case 1: /* mVA > mOA -- video is wider than display */ + lt_debug("%s: mVA > mOA\n", __func__); + switch (mCrop) { + case DISPLAY_AR_MODE_PANSCAN: + zoom = av_q2d(mVA) / av_q2d(mOA); + break; + case DISPLAY_AR_MODE_LETTERBOX: + break; + case DISPLAY_AR_MODE_PANSCAN2: + zoom = av_q2d(a149) / av_q2d(mOA); + break; + case DISPLAY_AR_MODE_NONE: + xzoom = av_q2d(mOA) / av_q2d(mVA); + zoom = av_q2d(mVA) / av_q2d(mOA); + break; + default: + break; + } + break; + case -1: /* mVA < mOA -- video is taller than display */ + lt_debug("%s: mVA < mOA\n", __func__); + switch (mCrop) { + case DISPLAY_AR_MODE_LETTERBOX: + break; + case DISPLAY_AR_MODE_PANSCAN2: + if (av_cmp_q(a149, mOA) < 0) { + zoom = av_q2d(mVA) * av_q2d(a149) / av_q2d(mOA); + break; + } + /* fallthrough for output format 14:9 */ + case DISPLAY_AR_MODE_PANSCAN: + zoom = av_q2d(mOA) / av_q2d(mVA); + break; + case DISPLAY_AR_MODE_NONE: + xzoom = av_q2d(mOA) / av_q2d(mVA); + break; + default: + break; + } + break; + } + lt_debug("zoom: %f xscale: %f xzoom: %f\n", zoom, xscale,xzoom); + clutter_actor_set_scale(vid_actor, xscale*zoom*xzoom, zoom); + } + clutter_timeline_stop(tl); + clutter_timeline_set_delay(tl, sleep_us/1000); + clutter_timeline_start(tl); +} + +void GLFbPC::bltOSDBuffer() +{ + // lt_info("%s\n", __func__); + int x = glfb_priv->mState.width; + int y = glfb_priv->mState.height; + ClutterContent *fb = clutter_image_new(); + if (!clutter_image_set_data(CLUTTER_IMAGE(fb), osd_buf->data(), COGL_PIXEL_FORMAT_BGRA_8888, x, y, x*4, NULL)) { + lt_info("GLFB::%s clutter_image_set_data failed?\n", __func__); + _exit(1); /* life is hard */ + } + clutter_actor_set_content(fb_actor, fb); + g_object_unref(fb); + clutter_actor_show(fb_actor); +} + +void GLFbPC::bltDisplayBuffer() +{ + // lt_info("GLFB::%s vdec: %p\n", __func__, vdec); + if (!vdec) /* cannot start yet */ + return; + static bool warn = true; + VDec::SWFramebuffer *buf = vdec->getDecBuf(); + if (!buf) { + if (warn) + lt_info("GLFB::%s did not get a buffer...\n", __func__); + warn = false; + return; + } + warn = true; + int w = buf->width(), h = buf->height(); + if (w == 0 || h == 0) + return; + + AVRational a = buf->AR(); + if (a.den != 0 && a.num != 0 && av_cmp_q(a, _mVA)) { + _mVA = a; + /* _mVA is the raw buffer's aspect, mVA is the real scaled output aspect */ + av_reduce(&mVA.num, &mVA.den, w * a.num, h * a.den, INT_MAX); + // mVA.num: 16 mVA.den: 9 w: 720 h: 576 + // 16*576/720/9 = 1.42222 + xscale = (double)mVA.num*h/(double)mVA.den/w; + mVAchanged = true; + } + + ClutterContent *fb = clutter_image_new(); + if (!clutter_image_set_data(CLUTTER_IMAGE(fb), &(*buf)[0], COGL_PIXEL_FORMAT_BGR_888, w, h, w*3, NULL)) { + lt_info("GLFB::%s clutter_image_set_data failed?\n", __func__); + _exit(1); /* life is hard */ + } + clutter_actor_set_content(vid_actor, fb); + g_object_unref(fb); + clutter_actor_show(vid_actor); + + /* "rate control" mechanism starts here... + * this implementation is pretty naive and not working too well, but + * better this than nothing... :-) */ + int64_t apts = 0; + int64_t vpts = buf->pts(); + if (adec) + apts = adec->getPts(); + if (apts != last_apts) { + int rate, dummy1, dummy2; + if (apts < vpts) + sleep_us = (sleep_us * 2 + (vpts - apts)*10/9) / 3; + else if (sleep_us > 1000) + sleep_us -= 1000; + last_apts = apts; + vdec->getPictureInfo(dummy1, dummy2, rate); + if (rate > 0) + rate = 2000000 / rate; /* limit to half the frame rate */ + else + rate = 50000; /* minimum 20 fps */ + if (sleep_us > rate) + sleep_us = rate; + else if (sleep_us < 1) + sleep_us = 1; + } + 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, vdec->buf_num); +} diff --git a/generic-pc/glfb.cpp b/generic-pc/glfb.cpp index 099f213..3811056 100644 --- a/generic-pc/glfb.cpp +++ b/generic-pc/glfb.cpp @@ -22,6 +22,7 @@ TODO: AV-Sync code is "experimental" at best */ +#include "config.h" #include #include diff --git a/generic-pc/glfb.h b/generic-pc/glfb.h index 78376ba..bc7b9f5 100644 --- a/generic-pc/glfb.h +++ b/generic-pc/glfb.h @@ -1,6 +1,6 @@ /* Copyright 2010 Carsten Juttner - Copyright 2012,2013 Stefan Seyfried + Copyright 2012,2013,2016 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 @@ -18,14 +18,20 @@ #ifndef __glthread__ #define __glthread__ +#include #include #include #include #include +#if USE_OPENGL #include #include #include #include /* for screeninfo etc. */ +#endif +#if USE_CLUTTER +#include +#endif extern "C" { #include } @@ -35,18 +41,15 @@ class GLFramebuffer : public OpenThreads::Thread public: GLFramebuffer(int x, int y); ~GLFramebuffer(); - void run(); std::vector *getOSDBuffer() { return &mOSDBuffer; } /* 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 setOutputFormat(AVRational a, int h, int c) { mOA = a; *mY = h; mCrop = c; mReInit = true; } void clear(); - fb_var_screeninfo getScreenInfo() { return screeninfo; } int getWindowID() { return GLWinID; } private: @@ -74,33 +77,45 @@ private: std::vector mOSDBuffer; /* silly bounce buffer */ +#if USE_OPENGL std::map mKeyMap; std::map mSpecialMap; +#endif +#if USE_CLUTTER + std::map mKeyMap; +#endif int input_fd; int64_t last_apts; static void rendercb(); /* callback for GLUT */ void render(); /* actual render function */ +#if USE_OPENGL static void keyboardcb(unsigned char key, int x, int y); static void specialcb(int key, int x, int y); static void resizecb(int w, int h); void checkReinit(int w, int h); /* e.g. in case window was resized */ + void setupGLObjects(); /* PBOs, textures and stuff */ + void releaseGLObjects(); + void drawSquare(float size, float x_factor = 1); /* do not be square */ +#endif +#if USE_CLUTTER + static bool keyboardcb(ClutterActor *actor, ClutterEvent *event, gpointer user_data); +#endif void initKeys(); /* setup key bindings for window */ void setupCtx(); /* create the window and make the context current */ void setupOSDBuffer(); /* create the OSD buffer */ - void setupGLObjects(); /* PBOs, textures and stuff */ - void releaseGLObjects(); - void drawSquare(float size, float x_factor = 1); /* do not be square */ struct { int width; /* width and height, fixed for a framebuffer instance */ int height; + bool blit; +#if USE_OPENGL GLuint osdtex; /* holds the OSD texture */ GLuint pbo; /* PBO we use for transfer to texture */ GLuint displaytex; /* holds the display texture */ GLuint displaypbo; - bool blit; +#endif } mState; void bltOSDBuffer(); diff --git a/generic-pc/video.cpp b/generic-pc/video.cpp index dcfb341..8237305 100644 --- a/generic-pc/video.cpp +++ b/generic-pc/video.cpp @@ -23,6 +23,7 @@ * TODO: buffer handling surely needs some locking... */ +#include "config.h" #include #include #include @@ -39,6 +40,13 @@ extern "C" { /* my own buf 256k */ #define DMX_BUF_SZ 0x20000 +#if USE_OPENGL +#define VDEC_PIXFMT AV_PIX_FMT_RGB32 +#endif +#if USE_CLUTTER +#define VDEC_PIXFMT AV_PIX_FMT_BGR24 +#endif + #include "video_lib.h" #include "dmx_hal.h" #include "glfb.h" @@ -294,9 +302,9 @@ void cVideo::ShowPicture(const char *fname) if (avpkt.size > len) lt_info("%s: WARN: pkt->size %d != len %d\n", __func__, avpkt.size, len); if (got_frame) { - unsigned int need = av_image_get_buffer_size(AV_PIX_FMT_RGB32, c->width, c->height, 1); + unsigned int need = av_image_get_buffer_size(VDEC_PIXFMT, c->width, c->height, 1); struct SwsContext *convert = sws_getContext(c->width, c->height, c->pix_fmt, - c->width, c->height, AV_PIX_FMT_RGB32, + c->width, c->height, VDEC_PIXFMT, SWS_BICUBIC, 0, 0, 0); if (!convert) lt_info("%s: ERROR setting up SWS context\n", __func__); @@ -305,7 +313,7 @@ void cVideo::ShowPicture(const char *fname) SWFramebuffer *f = &buffers[buf_in]; if (f->size() < need) f->resize(need); - av_image_fill_arrays(rgbframe->data, rgbframe->linesize, &(*f)[0], AV_PIX_FMT_RGB32, + av_image_fill_arrays(rgbframe->data, rgbframe->linesize, &(*f)[0], VDEC_PIXFMT, c->width, c->height, 1); sws_scale(convert, frame->data, frame->linesize, 0, c->height, rgbframe->data, rgbframe->linesize); @@ -538,10 +546,10 @@ void cVideo::run(void) lt_info("%s: WARN: pkt->size %d != len %d\n", __func__, avpkt.size, len); still_m.lock(); if (got_frame && ! stillpicture) { - unsigned int need = av_image_get_buffer_size(AV_PIX_FMT_RGB32, c->width, c->height, 1); + unsigned int need = av_image_get_buffer_size(VDEC_PIXFMT, c->width, c->height, 1); convert = sws_getCachedContext(convert, c->width, c->height, c->pix_fmt, - c->width, c->height, AV_PIX_FMT_RGB32, + c->width, c->height, VDEC_PIXFMT, SWS_BICUBIC, 0, 0, 0); if (!convert) lt_info("%s: ERROR setting up SWS context\n", __func__); @@ -550,7 +558,7 @@ void cVideo::run(void) SWFramebuffer *f = &buffers[buf_in]; if (f->size() < need) f->resize(need); - av_image_fill_arrays(rgbframe->data, rgbframe->linesize, &(*f)[0], AV_PIX_FMT_RGB32, + av_image_fill_arrays(rgbframe->data, rgbframe->linesize, &(*f)[0], VDEC_PIXFMT, c->width, c->height, 1); sws_scale(convert, frame->data, frame->linesize, 0, c->height, rgbframe->data, rgbframe->linesize);