diff --git a/Makefile b/Makefile index d80c7f62c92ee90533dbb31b90ab9d1ccc09be32..6c90da1617381c7b3c887d7441a3f37c418df0ee 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,8 @@ UNAME_S := $(shell uname -s) CXXFLAGS = -I./ CXXFLAGS += -g -Wall -Wformat -LIBS = +CXXFLAGS += `pkg-config --cflags mpv` +LIBS = `pkg-config --libs mpv` ##--------------------------------------------------------------------- ## OPENGL LOADER diff --git a/imgui.ini b/imgui.ini index b7ed50cf9cfde7e99d0eaa7d67933dff71760e9d..de57d2a63646551c564a582b5876fac13c06cbc5 100644 --- a/imgui.ini +++ b/imgui.ini @@ -4,12 +4,12 @@ Size=400,400 Collapsed=0 [Window][Hello, world!] -Pos=307,102 -Size=1086,775 +Pos=323,198 +Size=703,254 Collapsed=0 [Window][Dear ImGui Demo] -Pos=878,103 +Pos=1256,131 Size=550,680 Collapsed=0 diff --git a/main.cpp b/main.cpp index cd5bbba4176d73a5430ec40e659da198cc409f15..c2b892afc7b45eaab25ab2e47315615cd4617da0 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,36 @@ #include <stdio.h> #include <SDL.h> +// mpv stuff +#include <mpv/client.h> +#include <mpv/render_gl.h> + +// Ugly stuff for mpv +static Uint32 wakeup_on_mpv_render_update, wakeup_on_mpv_events; + +static void die(const char *msg) +{ + fprintf(stderr, "%s\n", msg); + exit(1); +} + +static void *get_proc_address_mpv(void *fn_ctx, const char *name) +{ + return SDL_GL_GetProcAddress(name); +} + +static void on_mpv_events(void *ctx) +{ + SDL_Event event = {.type = wakeup_on_mpv_events}; + SDL_PushEvent(&event); +} + +static void on_mpv_render_update(void *ctx) +{ + SDL_Event event = {.type = wakeup_on_mpv_render_update}; + SDL_PushEvent(&event); +} + // About Desktop OpenGL function loaders: // Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. // Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). @@ -29,8 +59,26 @@ using namespace gl; #endif // Main code -int main(int, char**) +int main(int argc, char **argv) { + // MPV init before SDL init + // If I remenber, we need to set an idle thing for mpv, so that it won't close even after the end of the video (which is facheux) + if (argc != 2) + die("pass a single media file as argument"); + + mpv_handle *mpv = mpv_create(); + if (!mpv) + die("context init failed"); + + // Some minor options can only be set before mpv_initialize(). + if (mpv_initialize(mpv) < 0) + die("mpv init failed"); + + mpv_request_log_messages(mpv, "debug"); + + // Jesus Christ SDL, you suck! + SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "no"); + // Setup SDL // (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems, // depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to latest version of SDL is recommended!) @@ -61,7 +109,7 @@ int main(int, char**) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); SDL_GLContext gl_context = SDL_GL_CreateContext(window); SDL_GL_MakeCurrent(window, gl_context); @@ -89,6 +137,63 @@ int main(int, char**) return 1; } + // Bind mpv and sdl + SDL_GLContext glcontext = SDL_GL_CreateContext(window); + if (!glcontext) + die("failed to create SDL GL context"); + + // TODO Name correctly those things + int __one[] = {1}; + auto __something = (mpv_opengl_init_params){ + .get_proc_address = get_proc_address_mpv, + }; + + mpv_render_param params[] = { + {MPV_RENDER_PARAM_API_TYPE, (void *) MPV_RENDER_API_TYPE_OPENGL}, + {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &__something}, + // Tell libmpv that you will call mpv_render_context_update() on render + // context update callbacks, and that you will _not_ block on the core + // ever (see <libmpv/render.h> "Threading" section for what libmpv + // functions you can call at all when this is active). + // In particular, this means you must call e.g. mpv_command_async() + // instead of mpv_command(). + // If you want to use synchronous calls, either make them on a separate + // thread, or remove the option below (this will disable features like + // DR and is not recommended anyway). + {MPV_RENDER_PARAM_ADVANCED_CONTROL, &__one}, + {(mpv_render_param_type)0} + }; + + // This makes mpv use the currently set GL context. It will use the callback + // (passed via params) to resolve GL builtin functions, as well as extensions. + mpv_render_context *mpv_gl; + if (mpv_render_context_create(&mpv_gl, mpv, params) < 0) + die("failed to initialize mpv GL context"); + + // We use events for thread-safe notification of the SDL main loop. + // Generally, the wakeup callbacks (set further below) should do as least + // work as possible, and merely wake up another thread to do actual work. + // On SDL, waking up the mainloop is the ideal course of action. SDL's + // SDL_PushEvent() is thread-safe, so we use that. + wakeup_on_mpv_render_update = SDL_RegisterEvents(1); + wakeup_on_mpv_events = SDL_RegisterEvents(1); + if (wakeup_on_mpv_render_update == (Uint32)-1 || + wakeup_on_mpv_events == (Uint32)-1) + die("could not register events"); + + // When normal mpv events are available. + mpv_set_wakeup_callback(mpv, on_mpv_events, NULL); + + // When there is a need to call mpv_render_context_update(), which can + // request a new frame to be rendered. + // (Separate from the normal event handling mechanism for the sake of + // users which run OpenGL on a different thread.) + mpv_render_context_set_update_callback(mpv_gl, on_mpv_render_update, NULL); + + // Play this file. + const char *cmd[] = {"loadfile", argv[1], NULL}; + mpv_command_async(mpv, 0, cmd); + // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); @@ -134,6 +239,7 @@ int main(int, char**) // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. SDL_Event event; + int redraw = 0; while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); @@ -141,6 +247,81 @@ int main(int, char**) done = true; if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_EXPOSED) + redraw = 1; + if (event.type == SDL_KEYDOWN) + { + if (event.key.keysym.sym == SDLK_SPACE) + { + const char *cmd_pause[] = {"cycle", "pause", NULL}; + mpv_command_async(mpv, 0, cmd_pause); + } + if (event.key.keysym.sym == SDLK_s) + { + // Also requires MPV_RENDER_PARAM_ADVANCED_CONTROL if you want + // screenshots to be rendered on GPU (like --vo=gpu would do). + const char *cmd_scr[] = {"screenshot-to-file", + "screenshot.png", + "window", + NULL}; + printf("attempting to save screenshot to %s\n", cmd_scr[1]); + mpv_command_async(mpv, 0, cmd_scr); + } + } + if (event.type == wakeup_on_mpv_render_update) + { + uint64_t flags = mpv_render_context_update(mpv_gl); + if (flags & MPV_RENDER_UPDATE_FRAME) + redraw = 1; + } + // Happens when at least 1 new event is in the mpv event queue. + if (event.type == wakeup_on_mpv_events) + { + // Handle all remaining mpv events. + while (1) { + mpv_event *mp_event = (mpv_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; + // Print log messages about DR allocations, just to + // test whether it works. If there is more than 1 of + // these, it works. (The log message can actually change + // any time, so it's possible this logging stops working + // in the future.) + if (strstr(msg->text, "DR image")) + printf("log: %s", msg->text); + continue; + } + printf("event: %s\n", mpv_event_name(mp_event->event_id)); + } + } + } + + if (redraw) + { + int w, h; + auto __thing1 = (mpv_opengl_fbo){ + .fbo = 0, + .w = w, + .h = h, + }; + int __one__one = 1; + SDL_GetWindowSize(window, &w, &h); + mpv_render_param params[] = { + // Specify the default framebuffer (0) as target. This will + // render onto the entire screen. If you want to show the video + // in a smaller rectangle or apply fancy transformations, you'll + // need to render into a separate FBO and draw it manually. + {MPV_RENDER_PARAM_OPENGL_FBO, &__thing1}, + // Flip rendering (needed due to flipped GL coordinate system). + {MPV_RENDER_PARAM_FLIP_Y, &__one__one}, + {(mpv_render_param_type)0} + }; + // See render_gl.h on what OpenGL environment mpv expects, and + // other API details. + mpv_render_context_render(mpv_gl, params); + SDL_GL_SwapWindow(window); } // Start the Dear ImGui frame @@ -194,6 +375,12 @@ int main(int, char**) SDL_GL_SwapWindow(window); } + // Destroy the GL renderer and all of the GL objects it allocated. If video + // is still running, the video track will be deselected. + mpv_render_context_free(mpv_gl); + + mpv_detach_destroy(mpv); + // Cleanup ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplSDL2_Shutdown();