mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2025-07-01 23:35:47 +00:00
Merge branch 'egl'
This commit is contained in:
commit
53641fabbf
@ -29,13 +29,20 @@ pkg_check_modules(XLIB x11-xcb)
|
|||||||
pkg_check_modules(LIBVA vdpau)
|
pkg_check_modules(LIBVA vdpau)
|
||||||
pkg_check_modules(PULSE libpulse-simple)
|
pkg_check_modules(PULSE libpulse-simple)
|
||||||
pkg_check_modules(CEC libcec>=3.0.0)
|
pkg_check_modules(CEC libcec>=3.0.0)
|
||||||
|
pkg_check_modules(EGL egl)
|
||||||
|
pkg_check_modules(GLES glesv2)
|
||||||
|
|
||||||
if(AVCODEC_FOUND AND AVUTIL_FOUND AND SDL_FOUND)
|
if(AVCODEC_FOUND AND AVUTIL_FOUND)
|
||||||
set(SOFTWARE_FOUND TRUE)
|
if(EGL_FOUND AND GLES_FOUND AND XLIB_FOUND)
|
||||||
if(XLIB_FOUND AND LIBVA_FOUND)
|
set(X11_FOUND TRUE)
|
||||||
set(VDPAU_FOUND TRUE)
|
endif()
|
||||||
else()
|
if(SDL_FOUND OR X11_FOUND)
|
||||||
set(VDPAU_FOUND FALSE)
|
set(SOFTWARE_FOUND TRUE)
|
||||||
|
if(XLIB_FOUND AND LIBVA_FOUND)
|
||||||
|
set(VDPAU_FOUND TRUE)
|
||||||
|
else()
|
||||||
|
set(VDPAU_FOUND FALSE)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
set(SOFTWARE_FOUND FALSE)
|
set(SOFTWARE_FOUND FALSE)
|
||||||
@ -52,9 +59,17 @@ elseif(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (SOFTWARE_FOUND)
|
if (SOFTWARE_FOUND)
|
||||||
list(APPEND SRC_LIST ./src/video/ffmpeg.c ./src/video/sdl.c ./src/audio/sdl.c)
|
list(APPEND SRC_LIST ./src/video/ffmpeg.c)
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL)
|
if (SDL_FOUND)
|
||||||
list(APPEND MOONLIGHT_OPTIONS SDL)
|
list(APPEND SRC_LIST ./src/video/sdl.c ./src/audio/sdl.c)
|
||||||
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL)
|
||||||
|
list(APPEND MOONLIGHT_OPTIONS SDL)
|
||||||
|
endif()
|
||||||
|
if (X11_FOUND)
|
||||||
|
list(APPEND SRC_LIST ./src/video/x11.c ./src/video/egl.c ./src/input/x11.c)
|
||||||
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_X11)
|
||||||
|
list(APPEND MOONLIGHT_OPTIONS X11)
|
||||||
|
endif()
|
||||||
if(VDPAU_FOUND)
|
if(VDPAU_FOUND)
|
||||||
list(APPEND SRC_LIST ./src/video/ffmpeg_vdpau.c)
|
list(APPEND SRC_LIST ./src/video/ffmpeg_vdpau.c)
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_VDPAU)
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_VDPAU)
|
||||||
@ -120,9 +135,19 @@ if(FREESCALE_FOUND)
|
|||||||
install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(SDL_FOUND)
|
||||||
|
target_include_directories(moonlight PRIVATE ${SDL_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(moonlight ${SDL_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(X11_FOUND)
|
||||||
|
target_include_directories(moonlight PRIVATE ${XLIB_INCLUDE_DIRS} ${EGL_INCLUDE_DIRS} ${GLES_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(moonlight ${XLIB_LIBRARIES} ${EGL_LIBRARIES} ${GLES_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
if (SOFTWARE_FOUND)
|
if (SOFTWARE_FOUND)
|
||||||
target_include_directories(moonlight PRIVATE ${SDL_INCLUDE_DIRS} ${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS})
|
target_include_directories(moonlight PRIVATE ${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS})
|
||||||
target_link_libraries(moonlight ${SDL_LIBRARIES} ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES})
|
target_link_libraries(moonlight ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES})
|
||||||
if(VDPAU_FOUND)
|
if(VDPAU_FOUND)
|
||||||
target_include_directories(moonlight PRIVATE ${XLIB_INCLUDE_DIRS} ${LIBVA_INCLUDE_DIRS})
|
target_include_directories(moonlight PRIVATE ${XLIB_INCLUDE_DIRS} ${LIBVA_INCLUDE_DIRS})
|
||||||
target_link_libraries(moonlight ${XLIB_LIBRARIES} ${LIBVA_LIBRARIES})
|
target_link_libraries(moonlight ${XLIB_LIBRARIES} ${LIBVA_LIBRARIES})
|
||||||
|
146
src/input/x11.c
Normal file
146
src/input/x11.c
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Moonlight Embedded.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Iwan Timmer
|
||||||
|
*
|
||||||
|
* Moonlight 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Moonlight 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 Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "x11.h"
|
||||||
|
#include "keyboard.h"
|
||||||
|
#include "../global.h"
|
||||||
|
#include "../loop.h"
|
||||||
|
|
||||||
|
#include <Limelight.h>
|
||||||
|
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
|
#define MODIFIERS (MODIFIER_SHIFT|MODIFIER_ALT|MODIFIER_CTRL)
|
||||||
|
|
||||||
|
static Display *display;
|
||||||
|
static Window window;
|
||||||
|
|
||||||
|
static Atom wm_deletemessage;
|
||||||
|
|
||||||
|
static int last_x = -1, last_y = -1;
|
||||||
|
static int keyboard_modifiers;
|
||||||
|
|
||||||
|
static const char data[1] = {0};
|
||||||
|
static Cursor cursor;
|
||||||
|
static bool grabbed = True;
|
||||||
|
|
||||||
|
static int x11_handler(int fd) {
|
||||||
|
XEvent event;
|
||||||
|
int button = 0;
|
||||||
|
int motion_x, motion_y;
|
||||||
|
|
||||||
|
XNextEvent(display, &event);
|
||||||
|
switch (event.type) {
|
||||||
|
case KeyPress:
|
||||||
|
case KeyRelease:
|
||||||
|
if (event.xkey.keycode >= 8 && event.xkey.keycode < (sizeof(keyCodes)/sizeof(keyCodes[0]) + 8)) {
|
||||||
|
if ((keyboard_modifiers & MODIFIERS) == MODIFIERS && event.type == KeyRelease) {
|
||||||
|
grabbed = !grabbed;
|
||||||
|
XDefineCursor(display, window, grabbed ? cursor : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int modifier = 0;
|
||||||
|
switch (event.xkey.keycode) {
|
||||||
|
case 0x32:
|
||||||
|
case 0x3e:
|
||||||
|
modifier = MODIFIER_SHIFT;
|
||||||
|
break;
|
||||||
|
case 0x40:
|
||||||
|
case 0x6c:
|
||||||
|
modifier = MODIFIER_ALT;
|
||||||
|
break;
|
||||||
|
case 0x25:
|
||||||
|
case 0x69:
|
||||||
|
modifier = MODIFIER_CTRL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modifier != 0) {
|
||||||
|
if (event.type == KeyPress)
|
||||||
|
keyboard_modifiers |= modifier;
|
||||||
|
else
|
||||||
|
keyboard_modifiers &= ~modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
short code = 0x80 << 8 | keyCodes[event.xkey.keycode - 8];
|
||||||
|
LiSendKeyboardEvent(code, event.type == KeyPress ? KEY_ACTION_DOWN : KEY_ACTION_UP, keyboard_modifiers);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ButtonPress:
|
||||||
|
case ButtonRelease:
|
||||||
|
switch (event.xbutton.button) {
|
||||||
|
case Button1:
|
||||||
|
button = BUTTON_LEFT;
|
||||||
|
break;
|
||||||
|
case Button2:
|
||||||
|
button = BUTTON_MIDDLE;
|
||||||
|
break;
|
||||||
|
case Button3:
|
||||||
|
button = BUTTON_RIGHT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button != 0)
|
||||||
|
LiSendMouseButtonEvent(event.type==ButtonPress ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE, button);
|
||||||
|
break;
|
||||||
|
case MotionNotify:
|
||||||
|
motion_x = event.xmotion.x - last_x;
|
||||||
|
motion_y = event.xmotion.y - last_y;
|
||||||
|
if (abs(motion_x) > 0 || abs(motion_y) > 0) {
|
||||||
|
if (last_x >= 0 && last_y >= 0)
|
||||||
|
LiSendMouseMoveEvent(motion_x, motion_y);
|
||||||
|
|
||||||
|
if (grabbed)
|
||||||
|
XWarpPointer(display, None, window, 0, 0, 0, 0, 640, 360);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_x = grabbed ? 640 : event.xmotion.x;
|
||||||
|
last_y = grabbed ? 360 : event.xmotion.y;
|
||||||
|
break;
|
||||||
|
case ClientMessage:
|
||||||
|
if (event.xclient.data.l[0] == wm_deletemessage)
|
||||||
|
quit();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LOOP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void x11_input_init(Display* x11_display, Window x11_window) {
|
||||||
|
display = x11_display;
|
||||||
|
window = x11_window;
|
||||||
|
|
||||||
|
wm_deletemessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
|
||||||
|
XSetWMProtocols(display, window, &wm_deletemessage, 1);
|
||||||
|
|
||||||
|
/* make a blank cursor */
|
||||||
|
XColor dummy;
|
||||||
|
Pixmap blank = XCreateBitmapFromData(display, window, data, 1, 1);
|
||||||
|
cursor = XCreatePixmapCursor(display, blank, blank, &dummy, &dummy, 0, 0);
|
||||||
|
XFreePixmap(display, blank);
|
||||||
|
XDefineCursor(display, window, cursor);
|
||||||
|
|
||||||
|
loop_add_fd(ConnectionNumber(display), x11_handler, POLLIN | POLLERR | POLLHUP);
|
||||||
|
}
|
22
src/input/x11.h
Normal file
22
src/input/x11.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Moonlight Embedded.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Iwan Timmer
|
||||||
|
*
|
||||||
|
* Moonlight 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Moonlight 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 Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
void x11_input_init(Display* display, Window window);
|
@ -55,6 +55,10 @@ enum platform platform_check(char* name) {
|
|||||||
return AML;
|
return AML;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_X11
|
||||||
|
if (std || strcmp(name, "x11") == 0)
|
||||||
|
return X11;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_SDL
|
#ifdef HAVE_SDL
|
||||||
if (std || strcmp(name, "sdl") == 0)
|
if (std || strcmp(name, "sdl") == 0)
|
||||||
return SDL;
|
return SDL;
|
||||||
@ -67,6 +71,10 @@ enum platform platform_check(char* name) {
|
|||||||
|
|
||||||
DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) {
|
DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) {
|
||||||
switch (system) {
|
switch (system) {
|
||||||
|
#ifdef HAVE_X11
|
||||||
|
case X11:
|
||||||
|
return &decoder_callbacks_x11;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_SDL
|
#ifdef HAVE_SDL
|
||||||
case SDL:
|
case SDL:
|
||||||
return &decoder_callbacks_sdl;
|
return &decoder_callbacks_sdl;
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
#define IS_EMBEDDED(SYSTEM) SYSTEM != SDL
|
#define IS_EMBEDDED(SYSTEM) SYSTEM != SDL
|
||||||
|
|
||||||
enum platform { NONE, SDL, PI, IMX, AML, FAKE };
|
enum platform { NONE, SDL, X11, PI, IMX, AML, FAKE };
|
||||||
|
|
||||||
enum platform platform_check(char*);
|
enum platform platform_check(char*);
|
||||||
PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system);
|
PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system);
|
||||||
@ -35,6 +35,9 @@ bool platform_supports_hevc(enum platform system);
|
|||||||
|
|
||||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake;
|
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake;
|
||||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_fake;
|
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_fake;
|
||||||
|
#ifdef HAVE_X11
|
||||||
|
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_SDL
|
#ifdef HAVE_SDL
|
||||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl;
|
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl;
|
||||||
void sdl_loop();
|
void sdl_loop();
|
||||||
|
199
src/video/egl.c
Normal file
199
src/video/egl.c
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Moonlight Embedded.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Iwan Timmer
|
||||||
|
*
|
||||||
|
* Moonlight 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Moonlight 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 Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "egl.h"
|
||||||
|
|
||||||
|
#include <Limelight.h>
|
||||||
|
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static const EGLint context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
||||||
|
static const char* texture_mappings[] = { "ymap", "umap", "vmap" };
|
||||||
|
static const char* vertex_source = "\
|
||||||
|
attribute vec2 position;\
|
||||||
|
varying mediump vec2 tex_position;\
|
||||||
|
\
|
||||||
|
void main() {\
|
||||||
|
gl_Position = vec4(position, 0, 1);\
|
||||||
|
tex_position = vec2((position.x + 1.) / 2., (1. - position.y) / 2.);\
|
||||||
|
}\
|
||||||
|
";
|
||||||
|
|
||||||
|
static const char* fragment_source = "\
|
||||||
|
uniform lowp sampler2D ymap;\
|
||||||
|
uniform lowp sampler2D umap;\
|
||||||
|
uniform lowp sampler2D vmap;\
|
||||||
|
varying mediump vec2 tex_position;\
|
||||||
|
\
|
||||||
|
void main() {\
|
||||||
|
mediump float y = texture2D(ymap, tex_position).r;\
|
||||||
|
mediump float u = texture2D(umap, tex_position).r - .5;\n\
|
||||||
|
mediump float v = texture2D(vmap, tex_position).r - .5;\n\
|
||||||
|
lowp float r = y + 1.28033 * v;\
|
||||||
|
lowp float g = y - .21482 * u - .38059 * v;\
|
||||||
|
lowp float b = y + 2.12798 * u;\
|
||||||
|
gl_FragColor = vec4(r, g, b, 1.0);\
|
||||||
|
}\
|
||||||
|
";
|
||||||
|
|
||||||
|
static const float vertices[] = {
|
||||||
|
-1.f, 1.f,
|
||||||
|
-1.f, -1.f,
|
||||||
|
1.f, -1.f,
|
||||||
|
1.f, 1.f
|
||||||
|
};
|
||||||
|
|
||||||
|
static const GLuint elements[] = {
|
||||||
|
0, 1, 2,
|
||||||
|
2, 3, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
static EGLDisplay display;
|
||||||
|
static EGLSurface surface;
|
||||||
|
static EGLContext context;
|
||||||
|
|
||||||
|
static int width, height;
|
||||||
|
static bool current;
|
||||||
|
|
||||||
|
static GLuint texture_id[3], texture_uniform[3];
|
||||||
|
static GLuint shader_program;
|
||||||
|
|
||||||
|
void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_window, int display_width, int display_height) {
|
||||||
|
width = display_width;
|
||||||
|
height = display_height;
|
||||||
|
|
||||||
|
// get an EGL display connection
|
||||||
|
display = eglGetDisplay(native_display);
|
||||||
|
if (display == EGL_NO_DISPLAY) {
|
||||||
|
fprintf( stderr, "EGL: error get display\n" );
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the EGL display connection
|
||||||
|
int major, minor;
|
||||||
|
EGLBoolean result = eglInitialize(display, &major, &minor);
|
||||||
|
if (result == EGL_FALSE) {
|
||||||
|
fprintf( stderr, "EGL: error initialising display\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get our config from the config class
|
||||||
|
EGLConfig config = NULL;
|
||||||
|
static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE };
|
||||||
|
|
||||||
|
EGLint totalConfigsFound = 0;
|
||||||
|
result = eglChooseConfig(display, attribute_list, &config, 1, &totalConfigsFound);
|
||||||
|
if (result != EGL_TRUE || totalConfigsFound == 0) {
|
||||||
|
fprintf(stderr, "EGL: Unable to query for available configs, found %d.\n", totalConfigsFound);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind the OpenGL API to the EGL
|
||||||
|
result = eglBindAPI(EGL_OPENGL_ES_API);
|
||||||
|
if (result == EGL_FALSE) {
|
||||||
|
fprintf(stderr, "EGL: error binding API\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an EGL rendering context
|
||||||
|
context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes);
|
||||||
|
if (context == EGL_NO_CONTEXT) {
|
||||||
|
fprintf(stderr, "EGL: couldn't get a valid context\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally we can create a new surface using this config and window
|
||||||
|
surface = eglCreateWindowSurface(display, config, (NativeWindowType) native_window, NULL);
|
||||||
|
eglMakeCurrent(display, surface, surface, context);
|
||||||
|
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
GLuint vbo;
|
||||||
|
glGenBuffers(1, &vbo);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
GLuint ebo;
|
||||||
|
glGenBuffers(1, &ebo);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource(vertex_shader, 1, &vertex_source, NULL);
|
||||||
|
glCompileShader(vertex_shader);
|
||||||
|
GLint maxLength = 0;
|
||||||
|
|
||||||
|
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(fragment_shader, 1, &fragment_source, NULL);
|
||||||
|
glCompileShader(fragment_shader);
|
||||||
|
|
||||||
|
shader_program = glCreateProgram();
|
||||||
|
glAttachShader(shader_program, vertex_shader);
|
||||||
|
glAttachShader(shader_program, fragment_shader);
|
||||||
|
|
||||||
|
glLinkProgram(shader_program);
|
||||||
|
glBindAttribLocation(shader_program, 0, "position");
|
||||||
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
|
||||||
|
|
||||||
|
glGenTextures(3, texture_id);
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture_id[i]);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, i > 0 ? width / 2 : width, i > 0 ? height / 2 : height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);
|
||||||
|
|
||||||
|
texture_uniform[i] = glGetUniformLocation(shader_program, texture_mappings[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void egl_draw(const uint8_t* image[3]) {
|
||||||
|
if (!current) {
|
||||||
|
eglMakeCurrent(display, surface, surface, context);
|
||||||
|
current = True;
|
||||||
|
}
|
||||||
|
|
||||||
|
glUseProgram(shader_program);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture_id[i]);
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, i > 0 ? width / 2 : width, i > 0 ? height / 2 : height, GL_LUMINANCE, GL_UNSIGNED_BYTE, image[i]);
|
||||||
|
glUniform1i(texture_uniform[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||||
|
|
||||||
|
eglSwapBuffers(display, surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void egl_destroy() {
|
||||||
|
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
|
eglDestroySurface(display, surface);
|
||||||
|
eglDestroyContext(display, context);
|
||||||
|
eglTerminate(display);
|
||||||
|
}
|
24
src/video/egl.h
Normal file
24
src/video/egl.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Moonlight Embedded.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Iwan Timmer
|
||||||
|
*
|
||||||
|
* Moonlight 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Moonlight 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 Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
|
||||||
|
void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_window, int display_width, int display_height);
|
||||||
|
void egl_draw(const uint8_t* image[3]);
|
||||||
|
void egl_destroy();
|
115
src/video/x11.c
Normal file
115
src/video/x11.c
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Moonlight Embedded.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Iwan Timmer
|
||||||
|
*
|
||||||
|
* Moonlight 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Moonlight 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 Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../video.h"
|
||||||
|
#include "../input/x11.h"
|
||||||
|
#include "egl.h"
|
||||||
|
#include "ffmpeg.h"
|
||||||
|
|
||||||
|
#include <Limelight.h>
|
||||||
|
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define DECODER_BUFFER_SIZE 92*1024
|
||||||
|
|
||||||
|
static char* ffmpeg_buffer = NULL;
|
||||||
|
|
||||||
|
static Display *display;
|
||||||
|
|
||||||
|
void x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||||
|
int avc_flags = SLICE_THREADING;
|
||||||
|
if (drFlags & FORCE_HARDWARE_ACCELERATION)
|
||||||
|
avc_flags |= HARDWARE_ACCELERATION;
|
||||||
|
|
||||||
|
if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2) < 0) {
|
||||||
|
fprintf(stderr, "Couldn't initialize video decoding\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||||
|
if (ffmpeg_buffer == NULL) {
|
||||||
|
fprintf(stderr, "Not enough memory\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
XInitThreads();
|
||||||
|
display = XOpenDisplay(NULL);
|
||||||
|
if (!display) {
|
||||||
|
fprintf(stderr, "Error: failed to open X display.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window root = DefaultRootWindow(display);
|
||||||
|
XSetWindowAttributes winattr = { .event_mask = PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask };
|
||||||
|
Window window = XCreateWindow(display, root, 0, 0, width, height, 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &winattr);
|
||||||
|
XMapWindow(display, window);
|
||||||
|
XStoreName(display, window, "Moonlight");
|
||||||
|
|
||||||
|
if (drFlags & DISPLAY_FULLSCREEN) {
|
||||||
|
Atom wm_state = XInternAtom(display, "_NET_WM_STATE", False);
|
||||||
|
Atom fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False);
|
||||||
|
|
||||||
|
XEvent xev = {0};
|
||||||
|
xev.type = ClientMessage;
|
||||||
|
xev.xclient.window = window;
|
||||||
|
xev.xclient.message_type = wm_state;
|
||||||
|
xev.xclient.format = 32;
|
||||||
|
xev.xclient.data.l[0] = 1;
|
||||||
|
xev.xclient.data.l[1] = fullscreen;
|
||||||
|
xev.xclient.data.l[2] = 0;
|
||||||
|
|
||||||
|
XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
|
||||||
|
}
|
||||||
|
|
||||||
|
egl_init(display, window, width, height);
|
||||||
|
x11_input_init(display, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void x11_cleanup() {
|
||||||
|
ffmpeg_destroy();
|
||||||
|
egl_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||||
|
if (decodeUnit->fullLength < DECODER_BUFFER_SIZE) {
|
||||||
|
PLENTRY entry = decodeUnit->bufferList;
|
||||||
|
int length = 0;
|
||||||
|
while (entry != NULL) {
|
||||||
|
memcpy(ffmpeg_buffer+length, entry->data, entry->length);
|
||||||
|
length += entry->length;
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
ffmpeg_decode(ffmpeg_buffer, length);
|
||||||
|
AVFrame* frame = ffmpeg_get_frame();
|
||||||
|
if (frame != NULL)
|
||||||
|
egl_draw(frame->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DECODER_RENDERER_CALLBACKS decoder_callbacks_x11 = {
|
||||||
|
.setup = x11_setup,
|
||||||
|
.cleanup = x11_cleanup,
|
||||||
|
.submitDecodeUnit = x11_submit_decode_unit,
|
||||||
|
.capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION | CAPABILITY_DIRECT_SUBMIT,
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user