Sélectionner une révision Git
module_sdl2.c 13,54 Kio
#include <lektor/module/module_sdl2.h>
#include <lektor/module/mpv.h>
#include <lektor/macro.h>
#include <lektor/defines.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <SDL.h>
#include <mpv/render_gl.h>
#include <mpv/client.h>
#define WIDTH 400
#define HEIGHT 200
struct module_sdl2_window {
/* Related to SDL2 */
SDL_Window *window;
SDL_GLContext glcontext;
/* Mpv related */
mpv_handle *mpv;
mpv_render_context *mpv_gl;
volatile int mpv_time_pos; // Don't write it in the database //
volatile int mpv_duration; // Because don't need to be persistent //
};
/* Private functions. */
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 inline void *
get_proc_address_mpv(void *fn_ctx, const char *name)
{
(void) fn_ctx;
return SDL_GL_GetProcAddress(name);
}
static inline void
on_mpv_events(void *ctx)
{
(void) ctx;
SDL_Event event = { .type = wakeup_on_mpv_events };
SDL_PushEvent(&event);
}
static inline void
on_mpv_render_update(void *ctx)
{
(void) ctx;
SDL_Event event = { .type = wakeup_on_mpv_render_update };
SDL_PushEvent(&event);
}
int
main(int argc, char *argv[])
{
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");
// Jesus Christ SDL, you suck!
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "no");
if (SDL_Init(SDL_INIT_VIDEO) < 0)
die("SDL init failed");
SDL_Window *window =
SDL_CreateWindow("hi", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1000, 500, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN |
SDL_WINDOW_RESIZABLE);
if (!window)
die("failed to create SDL window");
SDL_GLContext glcontext = SDL_GL_CreateContext(window);
if (!glcontext)
die("failed to create SDL GL context");
mpv_render_param params[] = {
{MPV_RENDER_PARAM_API_TYPE, MPV_RENDER_API_TYPE_OPENGL},
{
MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &(mpv_opengl_init_params)
{
.get_proc_address = get_proc_address_mpv,
}
},
// 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, &(int)
{
1
}
},
{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);
while (1) {
SDL_Event event;
if (SDL_WaitEvent(&event) != 1)
die("event loop error");
int redraw = 0;
switch (event.type) {
case SDL_QUIT:
goto done;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_EXPOSED)
redraw = 1;
break;
case 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);
}
break;
default:
// Happens when there is new work for the render thread (such as
// rendering a new video frame or redrawing it).
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_wait_event(mpv, 0);
if (mp_event->event_id == MPV_EVENT_NONE)
break;
printf("event: %s\n", mpv_event_name(mp_event->event_id));
}
}
}
if (redraw) {
int w, h;
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, &(mpv_opengl_fbo)
{
.fbo = 0,
.w = w,
.h = h,
}
},
{
MPV_RENDER_PARAM_FLIP_Y, &(int)
{
1
}
},
{0}
};
mpv_render_context_render(mpv_gl, params);
SDL_GL_SwapWindow(window);
}
}
done:
// 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);
printf("properly terminated\n");
return 0;
}
/* Exported functions */
extern int
module_set_function(void *arg__, void *handle__)
{
if (NULL == arg__ || handle__ == NULL)
return 1;
struct lkt_win *win = (struct lkt_win *) arg__;
win->new = module_sdl2_new;
win->close = module_sdl2_close;
win->free = module_sdl2_free;
win->toggle_pause = module_sdl2_toggle_pause;
win->load_file = module_sdl2_load_file;
win->set_volume = module_sdl2_set_volume;
win->is_paused = module_sdl2_is_paused;
win->get_duration = module_sdl2_get_duration;
win->get_elapsed = module_sdl2_get_elapsed;
win->handle_events = module_sdl2_handle_events;
win->handle = handle__;
return 0;
}
bool
module_sdl2_new(struct lkt_win *const win)
{
if (win == NULL)
return false;
struct module_sdl2_window *sdl2 = win->window;
int status;
if (sdl2 == NULL) {
sdl2 = calloc(1, sizeof(struct module_sdl2_window));
memset(sdl2, 0, sizeof(struct module_sdl2_window));
if (sdl2 == NULL) {
fprintf(stderr, " ! module_sdl2_new: failed to allocate window, no memory\n");
return false;
}
/* CREATE THE SDL2 WINDOW TODO */
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "no"); /* It seems that sdl sucks. */
if (SDL_Init(SDL_INIT_VIDEO) < 0)
goto error;
sdl2->window = SDL_CreateWindow("lektord", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
WIDTH, HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (!sdl2->window)
goto error;
sdl2->glcontext = SDL_GL_CreateContext(sdl2->window);
if (!sdl2->glcontext)
goto error;
/* Init mpv here */
sdl2->mpv = lmpv_prepare();
if ((status = mpv_initialize(sdl2->mpv)) < 0) {
fprintf(stderr, " ! lmpv_new: failed to initialize mpv: %s\n",
mpv_error_string(status));
goto error;
}
if (!lmpv_observe_properties(sdl2->mpv)) {
fprintf(stderr, " * lmpv_new: failed to observe properties\n");
goto error;
}
/* Init opengl with mpv and sdl2 */
mpv_render_param params[] = {
{ MPV_RENDER_PARAM_API_TYPE, MPV_RENDER_API_TYPE_OPENGL },
{
MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &(mpv_opengl_init_params)
{
.get_proc_address = get_proc_address_mpv,
}
},
{
MPV_RENDER_PARAM_ADVANCED_CONTROL, &(int)
{
1
}
},
{0}
};
if (mpv_render_context_create(&sdl2->mpv_gl, sdl2->mpv, params) < 0)
goto error;
fprintf(stderr, " . module_sdl2_new: successfully created the X11 window\n");
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)
goto error;
mpv_set_wakeup_callback(sdl2->mpv, on_mpv_events, NULL);
mpv_render_context_set_update_callback(sdl2->mpv_gl, on_mpv_render_update, NULL);
}
if (sdl2->mpv == NULL) {
sdl2->mpv = lmpv_prepare();
if ((status = mpv_initialize(sdl2->mpv)) < 0) {
fprintf(stderr, " ! lmpv_new: failed to initialize mpv: %s\n",
mpv_error_string(status));
goto error;
}
if (!lmpv_observe_properties(sdl2->mpv)) {
fprintf(stderr, " * lmpv_new: failed to observe properties\n");
goto error;
}
if (sdl2->mpv == NULL)
return false;
}
if (sdl2->mpv_gl == NULL) {
}
win->window = sdl2;
return true;
error:
return false;
}
void
module_sdl2_close(struct lkt_win *const win)
{
if (win == NULL || win->window == NULL)
return;
lmpv_free(&((struct module_sdl2_window *)win->window)->mpv);
}
void
module_sdl2_free(struct lkt_win *const win)
{
module_sdl2_close(win);
}
bool
module_sdl2_toggle_pause(struct lkt_win *const win, bool *new_paused)
{
if (win == NULL || win->window == NULL)
return false;
int pause, ret = ! lmpv_toggle_pause(((struct module_sdl2_window *) win->window)->mpv, &pause);
*new_paused = pause;
return ret;
}
bool
module_sdl2_load_file(struct lkt_win *const win, const char *filepath)
{
if (win == NULL || win->window == NULL)
return false;
struct module_sdl2_window *sdl2 = win->window;
bool ret = ! lmpv_load_file(sdl2->mpv, filepath);
sdl2->mpv_duration = 0;
sdl2->mpv_time_pos = 0;
return ret;
}
bool
module_sdl2_set_volume(struct lkt_win *const win, int vol)
{
if (win == NULL || win->window == NULL)
return false;
return ! lmpv_set_volume(((struct module_sdl2_window *) win->window)->mpv, vol);
}
bool
module_sdl2_is_paused(struct lkt_win *const win, bool *ret)
{
if (win == NULL || win->window == NULL)
return false;
int ret_, sta = ! lmpv_is_paused(((struct module_sdl2_window *) win->window)->mpv, &ret_);
*ret = ret_;
return sta;
}
bool
module_sdl2_get_duration(struct lkt_win *const win, int *dur_sec)
{
if (win == NULL || win->window == NULL)
return false;
*dur_sec = ((struct module_sdl2_window *) win->window)->mpv_duration;
return true;
}
bool
module_sdl2_get_elapsed(struct lkt_win *const win, int *elapsed_sec)
{
if (win == NULL || win->window == NULL)
return false;
*elapsed_sec = ((struct module_sdl2_window *) win->window)->mpv_time_pos;
return true;
}
bool
module_sdl2_handle_events(struct lkt_win *const win, sqlite3 *db, enum mpd_idle_flag *mpd_idle_events)
{
}