mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 15:02:58 +02:00
generic-pc: add alternative clutter based framebuffer
Add a framebuffer implementation based on clutter instead of "raw" OpenGL. The performance is slightly worse, but it might bring some platform abstraction and other benefits for the future.
This commit is contained in:
15
configure.ac
15
configure.ac
@@ -20,6 +20,18 @@ AC_DISABLE_STATIC
|
|||||||
AC_SYS_LARGEFILE
|
AC_SYS_LARGEFILE
|
||||||
AC_PROG_LIBTOOL
|
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
|
if test x"$BOXTYPE" = x"tripledragon"; then
|
||||||
PKG_CHECK_MODULES([DIRECTFB], [directfb])
|
PKG_CHECK_MODULES([DIRECTFB], [directfb])
|
||||||
fi
|
fi
|
||||||
@@ -54,6 +66,9 @@ if test x$BOXTYPE = xarmbox -a "$enable_gstreamer_10" = "yes"; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if test x$BOXTYPE = xgeneric -a x$BOXMODEL != xraspi; then
|
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([AVFORMAT], [libavformat >= 53.21.1])
|
||||||
PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0])
|
PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0])
|
||||||
# don't know which version is exactly needed here...
|
# don't know which version is exactly needed here...
|
||||||
|
@@ -1,30 +1,44 @@
|
|||||||
noinst_LTLIBRARIES = libgeneric.la
|
noinst_LTLIBRARIES = libgeneric.la
|
||||||
|
|
||||||
AM_CPPFLAGS = -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS
|
AM_CPPFLAGS = -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS
|
||||||
|
AM_CPPFLAGS += -Wfatal-errors
|
||||||
AM_CPPFLAGS += \
|
AM_CPPFLAGS += \
|
||||||
-I$(top_srcdir)/common \
|
-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_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
||||||
|
|
||||||
AM_LDFLAGS = \
|
AM_LDFLAGS = \
|
||||||
-lglut -lGL -lGLU -lGLEW -lao \
|
-lao \
|
||||||
-lOpenThreads \
|
-lOpenThreads \
|
||||||
@AVFORMAT_LIBS@ \
|
@AVFORMAT_LIBS@ \
|
||||||
@AVUTIL_LIBS@ \
|
@AVUTIL_LIBS@ \
|
||||||
@AVCODEC_LIBS@ \
|
@AVCODEC_LIBS@ \
|
||||||
@SWRESAMPLE_LIBS@ \
|
@SWRESAMPLE_LIBS@ \
|
||||||
@SWSCALE_LIBS@
|
@SWSCALE_LIBS@ \
|
||||||
|
@CLUTTER_LIBS@
|
||||||
|
|
||||||
|
if USE_OPENGL
|
||||||
|
AM_LDFLAGS += -lglut -lGL -lGLU -lGLEW -lao
|
||||||
|
endif
|
||||||
|
|
||||||
libgeneric_la_SOURCES = \
|
libgeneric_la_SOURCES = \
|
||||||
hardware_caps.c \
|
hardware_caps.c \
|
||||||
dmx.cpp \
|
dmx.cpp \
|
||||||
video.cpp \
|
video.cpp \
|
||||||
audio.cpp \
|
audio.cpp \
|
||||||
glfb.cpp \
|
|
||||||
init.cpp \
|
init.cpp \
|
||||||
record.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
|
if ENABLE_GSTREAMER_01
|
||||||
libgeneric_la_SOURCES += \
|
libgeneric_la_SOURCES += \
|
||||||
playback_gst_01.cpp
|
playback_gst_01.cpp
|
||||||
|
481
generic-pc/clutterfb.cpp
Normal file
481
generic-pc/clutterfb.cpp
Normal file
@@ -0,0 +1,481 @@
|
|||||||
|
/*
|
||||||
|
Framebuffer implementation using clutter https://developer.gnome.org/clutter/
|
||||||
|
Copyright (C) 2016 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
|
||||||
|
|
||||||
|
based on the openGL framebuffer implementation
|
||||||
|
Copyright 2010 Carsten Juttner <carjay@gmx.net>
|
||||||
|
Copyright 2012,2013 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
TODO: AV-Sync code is "experimental" at best
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include "glfb_priv.h"
|
||||||
|
#include "video_priv.h"
|
||||||
|
#include "audio_priv.h"
|
||||||
|
|
||||||
|
#include <clutter/x11/clutter-x11.h>
|
||||||
|
|
||||||
|
#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<unsigned char> &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<int, int>::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);
|
||||||
|
}
|
@@ -22,6 +22,7 @@
|
|||||||
TODO: AV-Sync code is "experimental" at best
|
TODO: AV-Sync code is "experimental" at best
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Carsten Juttner <carjay@gmx.net>
|
Copyright 2010 Carsten Juttner <carjay@gmx.net>
|
||||||
Copyright 2012,2013 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
|
Copyright 2012,2013,2016 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,14 +18,20 @@
|
|||||||
|
|
||||||
#ifndef __glthread__
|
#ifndef __glthread__
|
||||||
#define __glthread__
|
#define __glthread__
|
||||||
|
#include <config.h>
|
||||||
#include <OpenThreads/Thread>
|
#include <OpenThreads/Thread>
|
||||||
#include <OpenThreads/Mutex>
|
#include <OpenThreads/Mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#if USE_OPENGL
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
#include <GL/freeglut.h>
|
#include <GL/freeglut.h>
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
#include <linux/fb.h> /* for screeninfo etc. */
|
#include <linux/fb.h> /* for screeninfo etc. */
|
||||||
|
#endif
|
||||||
|
#if USE_CLUTTER
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
#endif
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavutil/rational.h>
|
#include <libavutil/rational.h>
|
||||||
}
|
}
|
||||||
@@ -35,18 +41,15 @@ class GLFramebuffer : public OpenThreads::Thread
|
|||||||
public:
|
public:
|
||||||
GLFramebuffer(int x, int y);
|
GLFramebuffer(int x, int y);
|
||||||
~GLFramebuffer();
|
~GLFramebuffer();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
std::vector<unsigned char> *getOSDBuffer() { return &mOSDBuffer; } /* pointer to OSD bounce buffer */
|
std::vector<unsigned char> *getOSDBuffer() { return &mOSDBuffer; } /* pointer to OSD bounce buffer */
|
||||||
|
|
||||||
int getOSDWidth() { return mState.width; }
|
int getOSDWidth() { return mState.width; }
|
||||||
int getOSDHeight() { return mState.height; }
|
int getOSDHeight() { return mState.height; }
|
||||||
void blit() { mState.blit = true; }
|
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 setOutputFormat(AVRational a, int h, int c) { mOA = a; *mY = h; mCrop = c; mReInit = true; }
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
fb_var_screeninfo getScreenInfo() { return screeninfo; }
|
|
||||||
int getWindowID() { return GLWinID; }
|
int getWindowID() { return GLWinID; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -74,33 +77,45 @@ private:
|
|||||||
|
|
||||||
std::vector<unsigned char> mOSDBuffer; /* silly bounce buffer */
|
std::vector<unsigned char> mOSDBuffer; /* silly bounce buffer */
|
||||||
|
|
||||||
|
#if USE_OPENGL
|
||||||
std::map<unsigned char, int> mKeyMap;
|
std::map<unsigned char, int> mKeyMap;
|
||||||
std::map<int, int> mSpecialMap;
|
std::map<int, int> mSpecialMap;
|
||||||
|
#endif
|
||||||
|
#if USE_CLUTTER
|
||||||
|
std::map<int, int> mKeyMap;
|
||||||
|
#endif
|
||||||
int input_fd;
|
int input_fd;
|
||||||
int64_t last_apts;
|
int64_t last_apts;
|
||||||
|
|
||||||
static void rendercb(); /* callback for GLUT */
|
static void rendercb(); /* callback for GLUT */
|
||||||
void render(); /* actual render function */
|
void render(); /* actual render function */
|
||||||
|
#if USE_OPENGL
|
||||||
static void keyboardcb(unsigned char key, int x, int y);
|
static void keyboardcb(unsigned char key, int x, int y);
|
||||||
static void specialcb(int key, int x, int y);
|
static void specialcb(int key, int x, int y);
|
||||||
static void resizecb(int w, int h);
|
static void resizecb(int w, int h);
|
||||||
void checkReinit(int w, int h); /* e.g. in case window was resized */
|
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 initKeys(); /* setup key bindings for window */
|
||||||
void setupCtx(); /* create the window and make the context current */
|
void setupCtx(); /* create the window and make the context current */
|
||||||
void setupOSDBuffer(); /* create the OSD buffer */
|
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 {
|
struct {
|
||||||
int width; /* width and height, fixed for a framebuffer instance */
|
int width; /* width and height, fixed for a framebuffer instance */
|
||||||
int height;
|
int height;
|
||||||
|
bool blit;
|
||||||
|
#if USE_OPENGL
|
||||||
GLuint osdtex; /* holds the OSD texture */
|
GLuint osdtex; /* holds the OSD texture */
|
||||||
GLuint pbo; /* PBO we use for transfer to texture */
|
GLuint pbo; /* PBO we use for transfer to texture */
|
||||||
GLuint displaytex; /* holds the display texture */
|
GLuint displaytex; /* holds the display texture */
|
||||||
GLuint displaypbo;
|
GLuint displaypbo;
|
||||||
bool blit;
|
#endif
|
||||||
} mState;
|
} mState;
|
||||||
|
|
||||||
void bltOSDBuffer();
|
void bltOSDBuffer();
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
* TODO: buffer handling surely needs some locking...
|
* TODO: buffer handling surely needs some locking...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -39,6 +40,13 @@ extern "C" {
|
|||||||
/* my own buf 256k */
|
/* my own buf 256k */
|
||||||
#define DMX_BUF_SZ 0x20000
|
#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 "video_lib.h"
|
||||||
#include "dmx_hal.h"
|
#include "dmx_hal.h"
|
||||||
#include "glfb.h"
|
#include "glfb.h"
|
||||||
@@ -294,9 +302,9 @@ void cVideo::ShowPicture(const char *fname)
|
|||||||
if (avpkt.size > len)
|
if (avpkt.size > len)
|
||||||
lt_info("%s: WARN: pkt->size %d != len %d\n", __func__, avpkt.size, len);
|
lt_info("%s: WARN: pkt->size %d != len %d\n", __func__, avpkt.size, len);
|
||||||
if (got_frame) {
|
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,
|
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);
|
SWS_BICUBIC, 0, 0, 0);
|
||||||
if (!convert)
|
if (!convert)
|
||||||
lt_info("%s: ERROR setting up SWS context\n", __func__);
|
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];
|
SWFramebuffer *f = &buffers[buf_in];
|
||||||
if (f->size() < need)
|
if (f->size() < need)
|
||||||
f->resize(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);
|
c->width, c->height, 1);
|
||||||
sws_scale(convert, frame->data, frame->linesize, 0, c->height,
|
sws_scale(convert, frame->data, frame->linesize, 0, c->height,
|
||||||
rgbframe->data, rgbframe->linesize);
|
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);
|
lt_info("%s: WARN: pkt->size %d != len %d\n", __func__, avpkt.size, len);
|
||||||
still_m.lock();
|
still_m.lock();
|
||||||
if (got_frame && ! stillpicture) {
|
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,
|
convert = sws_getCachedContext(convert,
|
||||||
c->width, c->height, c->pix_fmt,
|
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);
|
SWS_BICUBIC, 0, 0, 0);
|
||||||
if (!convert)
|
if (!convert)
|
||||||
lt_info("%s: ERROR setting up SWS context\n", __func__);
|
lt_info("%s: ERROR setting up SWS context\n", __func__);
|
||||||
@@ -550,7 +558,7 @@ void cVideo::run(void)
|
|||||||
SWFramebuffer *f = &buffers[buf_in];
|
SWFramebuffer *f = &buffers[buf_in];
|
||||||
if (f->size() < need)
|
if (f->size() < need)
|
||||||
f->resize(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);
|
c->width, c->height, 1);
|
||||||
sws_scale(convert, frame->data, frame->linesize, 0, c->height,
|
sws_scale(convert, frame->data, frame->linesize, 0, c->height,
|
||||||
rgbframe->data, rgbframe->linesize);
|
rgbframe->data, rgbframe->linesize);
|
||||||
|
Reference in New Issue
Block a user