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_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...
|
||||
|
@@ -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
|
||||
|
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
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <vector>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,14 +18,20 @@
|
||||
|
||||
#ifndef __glthread__
|
||||
#define __glthread__
|
||||
#include <config.h>
|
||||
#include <OpenThreads/Thread>
|
||||
#include <OpenThreads/Mutex>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#if USE_OPENGL
|
||||
#include <GL/glew.h>
|
||||
#include <GL/freeglut.h>
|
||||
#include <GL/gl.h>
|
||||
#include <linux/fb.h> /* for screeninfo etc. */
|
||||
#endif
|
||||
#if USE_CLUTTER
|
||||
#include <clutter/clutter.h>
|
||||
#endif
|
||||
extern "C" {
|
||||
#include <libavutil/rational.h>
|
||||
}
|
||||
@@ -35,18 +41,15 @@ class GLFramebuffer : public OpenThreads::Thread
|
||||
public:
|
||||
GLFramebuffer(int x, int y);
|
||||
~GLFramebuffer();
|
||||
|
||||
void run();
|
||||
std::vector<unsigned char> *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<unsigned char> mOSDBuffer; /* silly bounce buffer */
|
||||
|
||||
#if USE_OPENGL
|
||||
std::map<unsigned char, int> mKeyMap;
|
||||
std::map<int, int> mSpecialMap;
|
||||
#endif
|
||||
#if USE_CLUTTER
|
||||
std::map<int, int> 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();
|
||||
|
@@ -23,6 +23,7 @@
|
||||
* TODO: buffer handling surely needs some locking...
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
@@ -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);
|
||||
|
Reference in New Issue
Block a user