aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2022-10-02 07:40:14 +0200
committerdec05eba <dec05eba@protonmail.com>2022-10-02 07:45:16 +0200
commitbdd95325ce3b8b3f95316b206b3c4c210ef81c80 (patch)
treefedf3d44179a0b61def3b2522cd927c7aa64aa9c
parent24531105312edb362f8aa791698dc223c0c5a661 (diff)
Add mpv option
-rw-r--r--.gitignore1
-rw-r--r--README.md18
-rw-r--r--TODO7
-rwxr-xr-xbuild.sh5
-rw-r--r--include/mpv.hpp31
-rw-r--r--project.conf3
-rw-r--r--src/main.cpp187
-rw-r--r--src/mpv.cpp266
-rw-r--r--src/window_texture.c2
-rw-r--r--tests/main.cpp7
10 files changed, 483 insertions, 44 deletions
diff --git a/.gitignore b/.gitignore
index 1f413f9..439cf44 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,5 +5,6 @@ tests/sibs-build/
tests/compile_commands.json
vr-video-player
window_texture.o
+mpv.o
main.o
.clangd/
diff --git a/README.md b/README.md
index 13e008c..577e856 100644
--- a/README.md
+++ b/README.md
@@ -8,9 +8,22 @@ Might now work when using a compositor such as picom when using the glx backend.
# Building
Run `./build.sh` or if you are running Arch Linux, then you can find it on aur under the name vr-video-player-git (`yay -S vr-video-player-git`).\
-Dependencies needed when building using `build.sh`: `glm, glew, sdl2, openvr, libx11, libxcomposite, libfixes`.
+Dependencies needed when building using `build.sh`: `glm, glew, sdl2, openvr, libx11, libxcomposite, libxfixes, libmpv`.
# How to use
+vr-video-player has two options. Either capture a window and view it in vr or an work-in-progress built-in mpv option (slower at the moment).
+# Using the built-in video player
+To play a video with the built-in mpv player, run vr-video-player like so:
+```
+./vr-video-player --sphere --video <file-path>
+```
+for example:
+```
+./vr-video-player --sphere --video /home/adam/Videos/my-cool-vr-video.mp4
+```
+`--sphere` can be replaced with `--flat`, `plane` or `--sphere360` for different display modes.
+
+# Capturing a window
Install xdotool and launch a video in a video player (I recommend mpv, because browsers, smplayer and vlc player remove the vr for 360 videos) and resize it to fit your monitor or larger for best quality and then,
if you want to watch 180 degree stereoscopic videos then run:
@@ -49,6 +62,7 @@ Alternatively, you can run:
```
and vr-video-player will automatically select the focused window (and update when the focused window changes).
+# Input options
The video might not be in front of you, so to move the video in front of you, you can do any of the following:
* Pull the trigger on the vr controller
* Press "Alt + F1"
@@ -58,6 +72,8 @@ The video might not be in front of you, so to move the video in front of you, yo
You can zoom the view with alt + Q/E.
+When using the built-in video player and the vr window is focused you can then use left/right arrow keys to move back/forward in the video and space to pause. In the future vr-video-player will show a graphical interface inside vr to manipulate the video.
+
You can launch vr-video-player without any arguments to show a list of all arguments.
Note: If the cursor position is weird and does not match what you are seeing in stereoscopic vr mode, then try running the vr video player with the --cursor-wrap option:
diff --git a/TODO b/TODO
index e60dcdc..44a8ed1 100644
--- a/TODO
+++ b/TODO
@@ -1 +1,6 @@
-Use libmpv. Make stereo audio follow headset rotation in 180/360 mode (assume facing forward is the default audio mode).
+Use pointer/button motion event instead of XQueryPointer every frame.
+Use directional audio when using mpv.
+Optimize mpv rendering option. Causes stuttering for some reason while capturing a mpv window does not.
+Dynamically load mpv (with dlopen) when using the --video option instead of linking to it at compile time.
+Show mpv gui.
+Allow setting/changing video at runtime.
diff --git a/build.sh b/build.sh
index 7bf6226..1f2d44b 100755
--- a/build.sh
+++ b/build.sh
@@ -1,8 +1,9 @@
#!/bin/sh -e
-dependencies="glm glew sdl2 openvr x11 xcomposite xfixes"
+dependencies="glm glew sdl2 openvr x11 xcomposite xfixes mpv"
includes=$(pkg-config --cflags $dependencies)
libs=$(pkg-config --libs $dependencies)
gcc -c src/window_texture.c -O2 $includes
+g++ -c src/mpv.cpp -O2 $includes
g++ -c src/main.cpp -O2 $includes
-g++ -o vr-video-player -O2 window_texture.o main.o -s $libs
+g++ -o vr-video-player -O2 window_texture.o mpv.o main.o -s $libs
diff --git a/include/mpv.hpp b/include/mpv.hpp
new file mode 100644
index 0000000..6f3b559
--- /dev/null
+++ b/include/mpv.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <stdint.h>
+#include <SDL.h>
+
+typedef struct mpv_handle mpv_handle;
+typedef struct mpv_render_context mpv_render_context;
+
+class Mpv {
+public:
+ Mpv() = default;
+ ~Mpv();
+
+ bool create();
+ bool destroy();
+
+ bool load_file(const char *path);
+ // |width| and |ħeight| are set to 0 unless there is an event to reconfigure video size
+ void on_event(SDL_Event &event, bool *render_update, int64_t *width, int64_t *height, bool *quit);
+ void seek(double seconds);
+ void toggle_pause();
+ void draw(unsigned int framebuffer_id, int width, int height);
+
+ bool created = false;
+ uint32_t wakeup_on_mpv_render_update = -1;
+ uint32_t wakeup_on_mpv_events = -1;
+
+ mpv_handle *mpv = nullptr;
+ mpv_render_context *mpv_gl = nullptr;
+ bool paused = false;
+}; \ No newline at end of file
diff --git a/project.conf b/project.conf
index 154a611..701b959 100644
--- a/project.conf
+++ b/project.conf
@@ -11,4 +11,5 @@ sdl2 = "2"
openvr = "1"
x11 = "1"
xcomposite = ">=0.2"
-xfixes = ">=5" \ No newline at end of file
+xfixes = ">=5"
+mpv = ">=1" \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 0278da1..268a86d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -33,6 +33,7 @@
#include <GL/glew.h>
#include "../include/window_texture.h"
+#include "../include/mpv.hpp"
#include <SDL.h>
#include <SDL_opengl.h>
@@ -224,6 +225,8 @@ private: // OpenGL bookkeeping
FramebufferDesc leftEyeDesc;
FramebufferDesc rightEyeDesc;
+ FramebufferDesc mpvDesc;
+
bool CreateFrameBuffer( int nWidth, int nHeight, FramebufferDesc &framebufferDesc );
uint32_t m_nRenderWidth;
@@ -244,11 +247,17 @@ private: // X compositor
bool follow_focused = false;
bool focused_window_changed = true;
bool focused_window_set = false;
-
- int mouse_x;
- int mouse_y;
- int window_width;
- int window_height;
+ const char *mpv_file = nullptr;
+ Mpv mpv;
+ bool mpv_render_update = false;
+ int64_t mpv_video_width = 0;
+ int64_t mpv_video_height = 0;
+ bool mpv_video_loaded = false;
+
+ int mouse_x = 0;
+ int mouse_y = 0;
+ int window_width = 1;
+ int window_height = 1;
Uint32 window_resize_time;
bool window_resized = false;
@@ -258,8 +267,8 @@ private: // X compositor
int x_fixes_error_base;
int prev_visibility_state = VisibilityFullyObscured;
- GLint pixmap_texture_width = 0;
- GLint pixmap_texture_height = 0;
+ GLint pixmap_texture_width = 1;
+ GLint pixmap_texture_height = 1;
enum class ViewMode {
LEFT_RIGHT,
@@ -383,25 +392,26 @@ void dprintf( const char *fmt, ... )
}
static void usage() {
- fprintf(stderr, "usage: vr-video-player [--sphere|--sphere360|--flat|--plane] [--left-right|--right-left] [--stretch|--no-stretch] [--zoom zoom-level] [--cursor-scale scale] [--cursor-wrap|--no-cursor-wrap] [--follow-focused] <window_id>\n");
+ fprintf(stderr, "usage: vr-video-player [--sphere|--sphere360|--flat|--plane] [--left-right|--right-left] [--stretch|--no-stretch] [--zoom zoom-level] [--cursor-scale scale] [--cursor-wrap|--no-cursor-wrap] [--follow-focused] [--video video] <window_id>\n");
fprintf(stderr, "\n");
fprintf(stderr, "OPTIONS\n");
- fprintf(stderr, " --sphere View the window as a stereoscopic 180 degrees screen (half sphere). The view will be attached to your head in vr. This is recommended for 180 degrees videos. This is the default value\n");
- fprintf(stderr, " --sphere360 View the window as an equirectangular cube map. This is what is mostly used on youtube, where the video is split into top and bottom as a cubemap. The view will be attached to your head in vr\n");
- fprintf(stderr, " --flat View the window as a stereoscopic flat screen. This is recommended for stereoscopic videos and games\n");
- fprintf(stderr, " --left-right This option is used together with --flat, to specify if the left side of the window is meant to be viewed with the left eye and the right side is meant to be viewed by the right eye. This is the default value\n");
- fprintf(stderr, " --right-left This option is used together with --flat, to specify if the left side of the window is meant to be viewed with the right eye and the right side is meant to be viewed by the left eye\n");
- fprintf(stderr, " --plane View the window as a slightly curved screen. This is recommended for non-stereoscopic content\n");
- fprintf(stderr, " --stretch This option is used together with --flat, To specify if the size of both sides of the window should be combined and stretch to that size when viewed in vr. This is the default value\n");
- fprintf(stderr, " --no-stretch This option is used together with --flat, To specify if the size of one side of the window should be the size of the whole window when viewed in vr. This is the option you want if the window looks too wide\n");
- fprintf(stderr, " --zoom Change the distance to the window. This should be a positive value. In flat and plane modes, this is the distance to the window when the window is reset (with W key or controller trigger button). The default value is 0 for all modes except sphere mode, where the default value is 1. This value is unused for sphere360 mode\n");
- fprintf(stderr, " --cursor-scale Change the size of the cursor. This should be a positive value. If set to 0, then the cursor is hidden. The default value is 1 for all modes except sphere mode, where the default value is 0. The cursor is always hidden in sphere360 mode\n");
- fprintf(stderr, " --cursor-wrap If this option is set, then the cursor position in the vr view will wrap around when it reached the center of the window (i.e when it reaches the edge of one side of the stereoscopic view). This option is only valid for stereoscopic view (flat and sphere modes)\n");
- fprintf(stderr, " --no-cursor-wrap If this option is set, then the cursor position in the vr view will match the the real cursor position inside the window\n");
- fprintf(stderr, " --follow-focused If this option is set, then the selected window will be the focused window. vr-video-player will automatically update when the focused window changes. Either this option or window_id should be used\n");
- fprintf(stderr, " --free-camera If this option is set, then the camera wont follow your position. This option is only applicable when not using --sphere or --sphere360\n");
- fprintf(stderr, " --reduce-flicker A hack to reduce flickering in low resolution text when the headset is not moving by moving the window around quickly by a few pixels\n");
- fprintf(stderr, " window_id The X11 window id of the window to view in vr. This option or --follow-focused is required\n");
+ fprintf(stderr, " --sphere View the window as a stereoscopic 180 degrees screen (half sphere). The view will be attached to your head in vr. This is recommended for 180 degrees videos. This is the default value\n");
+ fprintf(stderr, " --sphere360 View the window as an equirectangular cube map. This is what is mostly used on youtube, where the video is split into top and bottom as a cubemap. The view will be attached to your head in vr\n");
+ fprintf(stderr, " --flat View the window as a stereoscopic flat screen. This is recommended for stereoscopic videos and games\n");
+ fprintf(stderr, " --left-right This option is used together with --flat, to specify if the left side of the window is meant to be viewed with the left eye and the right side is meant to be viewed by the right eye. This is the default value\n");
+ fprintf(stderr, " --right-left This option is used together with --flat, to specify if the left side of the window is meant to be viewed with the right eye and the right side is meant to be viewed by the left eye\n");
+ fprintf(stderr, " --plane View the window as a slightly curved screen. This is recommended for non-stereoscopic content\n");
+ fprintf(stderr, " --stretch This option is used together with --flat, To specify if the size of both sides of the window should be combined and stretch to that size when viewed in vr. This is the default value\n");
+ fprintf(stderr, " --no-stretch This option is used together with --flat, To specify if the size of one side of the window should be the size of the whole window when viewed in vr. This is the option you want if the window looks too wide\n");
+ fprintf(stderr, " --zoom <zoom> Change the distance to the window. This should be a positive value. In flat and plane modes, this is the distance to the window when the window is reset (with W key or controller trigger button). The default value is 0 for all modes except sphere mode, where the default value is 1. This value is unused for sphere360 mode\n");
+ fprintf(stderr, " --cursor-scale <scale> Change the size of the cursor. This should be a positive value. If set to 0, then the cursor is hidden. The default value is 1 for all modes except sphere mode, where the default value is 0. The cursor is always hidden in sphere360 mode\n");
+ fprintf(stderr, " --cursor-wrap If this option is set, then the cursor position in the vr view will wrap around when it reached the center of the window (i.e when it reaches the edge of one side of the stereoscopic view). This option is only valid for stereoscopic view (flat and sphere modes)\n");
+ fprintf(stderr, " --no-cursor-wrap If this option is set, then the cursor position in the vr view will match the the real cursor position inside the window\n");
+ fprintf(stderr, " --reduce-flicker A hack to reduce flickering in low resolution text when the headset is not moving by moving the window around quickly by a few pixels\n");
+ fprintf(stderr, " --free-camera If this option is set, then the camera wont follow your position\n");
+ fprintf(stderr, " --follow-focused If this option is set, then the selected window will be the focused window. vr-video-player will automatically update when the focused window changes. Either this option, --video or window_id should be used\n");
+ fprintf(stderr, " --video <video> Select the video to play (using mpv). Either this option, --follow-focused or window_id should be used\n");
+ fprintf(stderr, " window_id The X11 window id of the window to view in vr. Either this option, --follow-focused or --video should be used\n");
fprintf(stderr, "\n");
fprintf(stderr, "EXAMPLES\n");
fprintf(stderr, " vr-video-player 1830423\n");
@@ -409,6 +419,7 @@ static void usage() {
fprintf(stderr, " vr-video-player --flat --right-left 1830423\n");
fprintf(stderr, " vr-video-player --plane --zoom 2.0 1830423\n");
fprintf(stderr, " vr-video-player --flat $(xdotool selectwindow)\n");
+ fprintf(stderr, " vr-video-player --sphere $HOME/Videos/cool-vr-video.mp4\n");
fprintf(stderr, "\n");
fprintf(stderr, "Note: All options except window_id are optional\n");
exit(1);
@@ -526,10 +537,25 @@ CMainApplication::CMainApplication( int argc, char *argv[] )
cursor_wrap_set = true;
} else if(strcmp(argv[i], "--follow-focused") == 0) {
if(src_window_id) {
- fprintf(stderr, "Error: --follow-focused option can't be used together with the window_id option\n");
+ fprintf(stderr, "Error: window_id option can't be used together with the --follow-focused option\n");
+ exit(1);
+ }
+ if(mpv_file) {
+ fprintf(stderr, "Error: --video option can't be used together with the --follow-focused option\n");
exit(1);
}
follow_focused = true;
+ } else if(strcmp(argv[i], "--video") == 0 && i < argc - 1) {
+ if(src_window_id) {
+ fprintf(stderr, "Error: --follow-focused option can't be used together with the --video option\n");
+ exit(1);
+ }
+ if(follow_focused) {
+ fprintf(stderr, "Error: window_id option can't be used together with the --video option\n");
+ exit(1);
+ }
+ mpv_file = argv[i + 1];
+ ++i;
} else if(strcmp(argv[i], "--free-camera") == 0) {
free_camera = true;
} else if(strcmp(argv[i], "--reduce-flicker") == 0) {
@@ -539,7 +565,11 @@ CMainApplication::CMainApplication( int argc, char *argv[] )
usage();
} else {
if(follow_focused) {
- fprintf(stderr, "Error: window_id option can't be used together with the --follow-focused option\n");
+ fprintf(stderr, "Error: --follow-focused option can't be used together with the window_id option\n");
+ exit(1);
+ }
+ if(mpv_file) {
+ fprintf(stderr, "Error: --video option can't be used together with the window_id option\n");
exit(1);
}
if (strncmp(argv[i], "window:", 7) == 0) {
@@ -549,8 +579,8 @@ CMainApplication::CMainApplication( int argc, char *argv[] )
}
}
- if(src_window_id == None && !follow_focused) {
- fprintf(stderr, "Missing required window_id flag\n");
+ if(src_window_id == None && !follow_focused && !mpv_file) {
+ fprintf(stderr, "Missing required window_id, --follow-focused or --video option\n");
usage();
}
@@ -721,6 +751,9 @@ bool CMainApplication::BInit()
if( m_bDebugOpenGL )
SDL_GL_SetAttribute( SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG );
+ // Needed for mpv
+ SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "no");
+
m_pCompanionWindow = SDL_CreateWindow( "vr-video-player", nWindowPosX, nWindowPosY, m_nCompanionWindowWidth, m_nCompanionWindowHeight, unWindowFlags );
if (m_pCompanionWindow == NULL)
{
@@ -778,6 +811,13 @@ bool CMainApplication::BInit()
return false;
}
+ if(mpv_file) {
+ if(!mpv.create())
+ return false;
+
+ mpv.load_file(mpv_file);
+ }
+
//char cwd[4096];
//getcwd(cwd, sizeof(cwd));
//printf("cwd: %s\n", cwd);
@@ -901,6 +941,9 @@ void CMainApplication::Shutdown()
if( m_pContext )
{
+ if(mpv_file)
+ mpv.destroy();
+
if( m_bDebugOpenGL )
{
glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_FALSE );
@@ -931,6 +974,12 @@ void CMainApplication::Shutdown()
glDeleteTextures( 1, &rightEyeDesc.m_nResolveTextureId );
glDeleteFramebuffers( 1, &rightEyeDesc.m_nResolveFramebufferId );
+ glDeleteRenderbuffers( 1, &mpvDesc.m_nDepthBufferId );
+ glDeleteTextures( 1, &mpvDesc.m_nRenderTextureId );
+ glDeleteFramebuffers( 1, &mpvDesc.m_nRenderFramebufferId );
+ glDeleteTextures( 1, &mpvDesc.m_nResolveTextureId );
+ glDeleteFramebuffers( 1, &mpvDesc.m_nResolveFramebufferId );
+
if( m_unCompanionWindowVAO != 0 )
{
glDeleteVertexArrays( 1, &m_unCompanionWindowVAO );
@@ -999,6 +1048,10 @@ bool CMainApplication::HandleInput()
SDL_Event sdlEvent;
bool bRet = false;
zoom_resize = false;
+ mpv_render_update = false;
+ int64_t video_width = 0;
+ int64_t video_height = 0;
+ bool mpv_quit = false;
while ( SDL_PollEvent( &sdlEvent ) != 0 )
{
@@ -1024,6 +1077,38 @@ bool CMainApplication::HandleInput()
{
zoom_out();
}
+ if(mpv_file && sdlEvent.key.keysym.sym == SDLK_LEFT)
+ {
+ mpv.seek(-5.0); // Seek backwards 5 seconds
+ }
+ if(mpv_file && sdlEvent.key.keysym.sym == SDLK_RIGHT)
+ {
+ mpv.seek(5.0); // Seek forwards 5 seconds
+ }
+ if(mpv_file && sdlEvent.key.keysym.sym == SDLK_SPACE)
+ {
+ mpv.toggle_pause();
+ }
+ }
+
+ bool opdoot = false;
+ if(mpv_file)
+ mpv.on_event(sdlEvent, &opdoot, &video_width, &video_height, &mpv_quit);
+ mpv_render_update |= opdoot;
+
+ if(mpv_quit)
+ bRet = true;
+
+ // TODO: Allow resize config
+ if(video_width > 0 && video_height > 0 && video_width != mpv_video_width && video_height != mpv_video_height && !mpv_video_loaded) {
+ mpv_video_loaded = true;
+ mpv_video_width = video_width;
+ mpv_video_height = video_height;
+ pixmap_texture_width = mpv_video_width;
+ pixmap_texture_height = mpv_video_height;
+ // TODO: Do not create depth buffer and extra framebuffers
+ CreateFrameBuffer(mpv_video_width, mpv_video_height, mpvDesc);
+ SetupScene();
}
}
@@ -1118,6 +1203,10 @@ bool CMainApplication::HandleInput()
glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&window_texture));
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &pixmap_texture_width);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &pixmap_texture_height);
+ if(pixmap_texture_width == 0)
+ pixmap_texture_width = 1;
+ if(pixmap_texture_height == 0)
+ pixmap_texture_height = 1;
glBindTexture(GL_TEXTURE_2D, 0);
SetupScene();
} else if(!window_resized && zoom_resize) {
@@ -2084,6 +2173,39 @@ void CMainApplication::RenderStereoTargets()
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
glEnable( GL_MULTISAMPLE );
+ // Mpv
+ if(mpv_file && mpv_video_loaded) {
+ glBindFramebuffer( GL_FRAMEBUFFER, mpvDesc.m_nRenderFramebufferId );
+ glViewport(0, 0, mpv_video_width, mpv_video_height);
+ if(mpv_render_update) {
+ mpv_render_update = false;
+ glDisable(GL_DEPTH_TEST);
+
+ glBindVertexArray( m_unCompanionWindowVAO );
+ glUseProgram( m_unCompanionWindowProgramID );
+
+ mpv.draw(mpvDesc.m_nRenderFramebufferId, mpv_video_width, mpv_video_height);
+
+ glBindVertexArray( 0 );
+ glUseProgram( 0 );
+ }
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+
+ glDisable( GL_MULTISAMPLE );
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mpvDesc.m_nRenderFramebufferId );
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mpvDesc.m_nResolveFramebufferId );
+
+ glBlitFramebuffer( 0, 0, mpv_video_width, mpv_video_height, 0, 0, mpv_video_width, mpv_video_height,
+ GL_COLOR_BUFFER_BIT,
+ GL_LINEAR );
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0 );
+
+ glEnable( GL_MULTISAMPLE );
+ }
+
// Left Eye
glBindFramebuffer( GL_FRAMEBUFFER, leftEyeDesc.m_nRenderFramebufferId );
glViewport(0, 0, m_nRenderWidth, m_nRenderHeight );
@@ -2122,6 +2244,8 @@ void CMainApplication::RenderStereoTargets()
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0 );
+ //glEnable( GL_MULTISAMPLE );
+
//glBindTexture(GL_TEXTURE_2D, 0);
}
@@ -2131,7 +2255,7 @@ void CMainApplication::RenderStereoTargets()
//-----------------------------------------------------------------------------
void CMainApplication::RenderScene( vr::Hmd_Eye nEye )
{
- if(!src_window_id)
+ if(!src_window_id && !mpv_file)
return;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -2197,10 +2321,11 @@ void CMainApplication::RenderScene( vr::Hmd_Eye nEye )
glBindVertexArray( m_unSceneVAO );
glActiveTexture(GL_TEXTURE0);
- glBindTexture( GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&window_texture) );
+ glBindTexture(GL_TEXTURE_2D, mpv_file ? mpvDesc.m_nResolveTextureId : window_texture_get_opengl_texture_id(&window_texture));
glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, arrow_image_texture_id);
+ glBindTexture(GL_TEXTURE_2D, mpv_file ? 0 : arrow_image_texture_id);
glDrawArrays( GL_TRIANGLES, 0, m_uiVertcount );
+
glBindVertexArray( 0 );
glActiveTexture(GL_TEXTURE0);
diff --git a/src/mpv.cpp b/src/mpv.cpp
new file mode 100644
index 0000000..4d5e391
--- /dev/null
+++ b/src/mpv.cpp
@@ -0,0 +1,266 @@
+#include "../include/mpv.hpp"
+#include <mpv/client.h>
+#include <mpv/render_gl.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+static bool exec_program_daemonized(const char **args) {
+ /* 1 argument */
+ if(args[0] == nullptr)
+ return false;
+
+ pid_t pid = vfork();
+ if(pid == -1) {
+ perror("Failed to vfork");
+ return false;
+ } else if(pid == 0) { /* child */
+ setsid();
+ signal(SIGHUP, SIG_IGN);
+
+ // Daemonize child to make the parent the init process which will reap the zombie child
+ pid_t second_child = vfork();
+ if(second_child == 0) { // child
+ execvp(args[0], (char* const*)args);
+ perror("execvp");
+ _exit(127);
+ } else if(second_child != -1) {
+ // TODO:
+ _exit(0);
+ }
+ } else { /* parent */
+ waitpid(pid, nullptr, 0);
+ }
+
+ return true;
+}
+
+static void show_notification(const char *title, const char *msg, const char *urgency) {
+ const char *args[] = { "notify-send", "-t", "10000", "-u", urgency, "--", title, msg, NULL };
+ exec_program_daemonized(args);
+}
+
+static void* get_proc_address_mpv(void*, const char *name) {
+ return SDL_GL_GetProcAddress(name);
+}
+
+static void on_mpv_events(void *ctx) {
+ Mpv *mpv = (Mpv*)ctx;
+ SDL_Event event;
+ event.type = mpv->wakeup_on_mpv_events;
+ SDL_PushEvent(&event);
+}
+
+static void on_mpv_render_update(void *ctx) {
+ Mpv *mpv = (Mpv*)ctx;
+ SDL_Event event;
+ event.type = mpv->wakeup_on_mpv_render_update;
+ SDL_PushEvent(&event);
+}
+
+Mpv::~Mpv() {
+ destroy();
+}
+
+bool Mpv::create() {
+ if(created)
+ return false;
+
+ mpv = mpv_create();
+ if(!mpv) {
+ fprintf(stderr, "Error: mpv_create failed\n");
+ return false;
+ }
+
+ if(mpv_initialize(mpv) < 0) {
+ fprintf(stderr, "Error: mpv_initialize failed\n");
+ mpv_destroy(mpv);
+ mpv = nullptr;
+ return false;
+ }
+
+ //mpv_request_log_messages(mpv, "debug");
+
+ mpv_opengl_init_params gl_init_params;
+ memset(&gl_init_params, 0, sizeof(gl_init_params));
+ gl_init_params.get_proc_address = get_proc_address_mpv;
+
+ int advanced_control = 1;
+
+ mpv_render_param params[] = {
+ { MPV_RENDER_PARAM_API_TYPE, (void*)MPV_RENDER_API_TYPE_OPENGL },
+ { MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params },
+ { MPV_RENDER_PARAM_ADVANCED_CONTROL, &advanced_control },
+ //{ MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME, 0 }, // TODO: Manually sync after this
+ { MPV_RENDER_PARAM_INVALID, 0 }
+ };
+
+ mpv_set_option_string(mpv, "vd-lavc-dr", "yes");
+ mpv_set_option_string(mpv, "vo", "libmpv");
+ mpv_set_option_string(mpv, "hwdec", "auto");
+ mpv_set_option_string(mpv, "profile", "gpu-hq");
+ mpv_set_option_string(mpv, "gpu-api", "opengl");
+
+ if(mpv_render_context_create(&mpv_gl, mpv, params) < 0) {
+ fprintf(stderr, "Error: mpv_render_context_create failed\n");
+ mpv_destroy(mpv);
+ mpv = nullptr;
+ mpv_gl = nullptr;
+ return false;
+ }
+
+ wakeup_on_mpv_render_update = SDL_RegisterEvents(1);
+ wakeup_on_mpv_events = SDL_RegisterEvents(1);
+ if(wakeup_on_mpv_render_update == (uint32_t)-1 || wakeup_on_mpv_events == (uint32_t)-1) {
+ fprintf(stderr, "Error: SDL_RegisterEvents failed\n");
+ // TODO: Remove registered events?
+ wakeup_on_mpv_render_update = -1;
+ wakeup_on_mpv_events = -1;
+ mpv_render_context_free(mpv_gl);
+ mpv_destroy(mpv);
+ mpv = nullptr;
+ mpv_gl = nullptr;
+ return false;
+ }
+
+ mpv_set_wakeup_callback(mpv, on_mpv_events, this);
+ mpv_render_context_set_update_callback(mpv_gl, on_mpv_render_update, this);
+
+ created = true;
+ return true;
+}
+
+bool Mpv::destroy() {
+ if(!created)
+ return true;
+
+ if(mpv_gl)
+ mpv_render_context_free(mpv_gl);
+ if(mpv)
+ mpv_destroy(mpv);
+
+ created = false;
+ return true;
+}
+
+bool Mpv::load_file(const char *path) {
+ if(!created)
+ return false;
+
+ const char *cmd[] = { "loadfile", path, nullptr };
+ mpv_command_async(mpv, 0, cmd);
+ return true;
+}
+
+void Mpv::on_event(SDL_Event &event, bool *render_update, int64_t *width, int64_t *height, bool *quit) {
+ if(render_update)
+ *render_update = false;
+
+ if(width)
+ *width = 0;
+
+ if(height)
+ *height = 0;
+
+ if(quit)
+ *quit = false;
+
+ if(!created)
+ return;
+
+ if(event.type == wakeup_on_mpv_render_update) {
+ uint64_t flags = mpv_render_context_update(mpv_gl);
+ if(flags & MPV_RENDER_UPDATE_FRAME) {
+ if(render_update)
+ *render_update = true;
+ }
+ }
+
+ if(event.type == wakeup_on_mpv_events) {
+ while(true) {
+ mpv_event *mp_event = mpv_wait_event(mpv, 0);
+ if(mp_event->event_id == MPV_EVENT_NONE)
+ break;
+
+ if(mp_event->event_id == MPV_EVENT_LOG_MESSAGE) {
+ mpv_event_log_message *msg = (mpv_event_log_message*)mp_event->data;
+ //printf("log: %s", msg->text);
+ } else {
+ //printf("mpv event: %s\n", mpv_event_name(mp_event->event_id));
+ }
+
+ if(mp_event->event_id == MPV_EVENT_END_FILE) {
+ mpv_event_end_file *msg = (mpv_event_end_file*)mp_event->data;
+ if(msg->reason == MPV_END_FILE_REASON_ERROR) {
+ show_notification("vr video player mpv video error", mpv_error_string(msg->error), "critical");
+ exit(6);
+ }
+ if(msg->reason == MPV_END_FILE_REASON_EOF) {
+ show_notification("vr video player", "the video ended", "low");
+ if(quit)
+ *quit = true;
+ }
+ }
+
+ if(mp_event->event_id == MPV_EVENT_VIDEO_RECONFIG) {
+ int64_t new_width = 0;
+ mpv_get_property(mpv, "width", MPV_FORMAT_INT64, &new_width);
+
+ int64_t new_height = 0;
+ mpv_get_property(mpv, "height", MPV_FORMAT_INT64, &new_height);
+
+ if(width)
+ *width = new_width;
+
+ if(height)
+ *height = new_height;
+ }
+ }
+ }
+}
+
+void Mpv::seek(double seconds) {
+ if(!created)
+ return;
+
+ char seconds_str[128];
+ snprintf(seconds_str, sizeof(seconds_str), "%f", seconds);
+
+ const char *cmd[] = { "seek", seconds_str, nullptr };
+ mpv_command_async(mpv, 0, cmd);
+}
+
+void Mpv::toggle_pause() {
+ if(!created)
+ return;
+
+ paused = !paused;
+ int pause_value = paused ? 1 : 0;
+ mpv_set_property_async(mpv, 0, "pause", MPV_FORMAT_FLAG, &pause_value);
+}
+
+void Mpv::draw(unsigned int framebuffer_id, int width, int height) {
+ if(!created)
+ return;
+
+ mpv_opengl_fbo fbo;
+ memset(&fbo, 0, sizeof(fbo));
+ fbo.fbo = framebuffer_id;
+ fbo.w = width;
+ fbo.h = height;
+
+ int flip_y = 0;
+ int shit = 1;
+
+ mpv_render_param params[] = {
+ { MPV_RENDER_PARAM_OPENGL_FBO, &fbo },
+ { MPV_RENDER_PARAM_FLIP_Y, &flip_y },
+ //{ MPV_RENDER_PARAM_SKIP_RENDERING, &shit },
+ { MPV_RENDER_PARAM_INVALID, 0 }
+ };
+
+ int res = mpv_render_context_render(mpv_gl, params);
+ //fprintf(stderr, "draw mpv: %d\n", res);
+} \ No newline at end of file
diff --git a/src/window_texture.c b/src/window_texture.c
index 0479254..54c598d 100644
--- a/src/window_texture.c
+++ b/src/window_texture.c
@@ -70,7 +70,7 @@ int window_texture_on_resize(WindowTexture *self) {
GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT,
GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
/*GLX_BIND_TO_MIPMAP_TEXTURE_EXT, True,*/
- GLX_BUFFER_SIZE, 24,
+ GLX_BUFFER_SIZE, 24,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
diff --git a/tests/main.cpp b/tests/main.cpp
deleted file mode 100644
index 9ad80a6..0000000
--- a/tests/main.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include <stdio.h>
-
-int main(int argc, char **argv)
-{
- printf("hello, world!\n");
- return 0;
-}