mirror of
https://github.com/tuxbox-fork-migrations/recycled-ni-libstb-hal.git
synced 2025-08-26 23:12:44 +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.
Origin commit data
------------------
Branch: master
Commit: dd6921140d
Author: Stefan Seyfried <seife@tuxbox-git.slipkontur.de>
Date: 2016-10-30 (Sun, 30 Oct 2016)
------------------
This commit was generated by Migit
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