mpv-examples
mpv-examples copied to clipboard
Rendering OpenGL with the Enlightenment Foundation Library
Hello MPV experts,
I tried to adapt the SDL example to Efl/Elementary and to render the mpv context to a Glview widget. Unfortunately MPV doesn't render to the Glview widget. Sometimes there is no video window, sometimes a seperate video window is shown. I really don't know, how to get ahead, and would be very thankful for every tip.
Thanks in advance and a happy New Year 2022, Max
Here is my current code: (you can find it here):
// Build with: gcc -o mpv mpv.c `pkg-config --cflags --libs mpv elementary`
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <Elementary.h>
#include <mpv/client.h>
#include <mpv/render_gl.h>
static int count = 0;
static int render_event;
typedef struct _cb_data cbd;
struct _cb_data
{
Evas_Object *glview;
mpv_handle *mpv;
mpv_render_context *mpv_gl;
GLuint fbo;
};
static cbd *cb_data = NULL;
static void die(const char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
static void *get_proc_address(void *fn_ctx, const char *name)
{
Evas_Object *glview = fn_ctx;
Evas_GL *gl = elm_glview_evas_gl_get(glview);
void *addr = evas_gl_proc_address_get(gl,name);
return addr;
}
void _render_update(void *ctx) {
printf("CALL RENDER %d\n",count);
count = 1;
}
Eina_Bool render_event_cb(void *data, int type, void *ev) {
elm_glview_changed_set(cb_data->glview);
return 1;
}
void _on_render(Evas_Object *obj)
{
mpv_render_context *mpv_gl = cb_data->mpv_gl;
Evas_Object *gl = cb_data->glview;
int w, h;
elm_glview_size_get(gl, &w, &h);
printf("RENDER %d %d\n",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,
}},
// Flip rendering (needed due to flipped GL coordinate system).
{MPV_RENDER_PARAM_FLIP_Y, &(int){1}},
{0}
};
// See render_gl.h on what OpenGL environment mpv expects, and
// other API details.
mpv_render_context_render(mpv_gl, params);
}
static Eina_Bool
on_mpv(void *data)
{
mpv_handle *mpv = data;
while (1) {
//printf("COUNT %d",count);
if (count) {
count = 0;
ecore_event_add(render_event,NULL, NULL,NULL);
}
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 = 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;
}
// ´
}
// Let the event continue to other callbacks which have not been called yet
return ECORE_CALLBACK_PASS_ON;
}
void _init_gl(Evas_Object *glview) {
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, glview,
}},
// 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, cb_data->mpv, params) < 0)
die("failed to initialize mpv GL context");
cb_data->mpv_gl = mpv_gl;
// 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.)
render_event = ecore_event_type_new();
ecore_event_handler_add(render_event,render_event_cb,NULL);
mpv_render_context_set_update_callback(mpv_gl, _render_update, NULL);
}
int main(int argc, char *argv[])
{
Evas_Object *win, *bx, *gl;
if (!(cb_data = calloc(1, sizeof(cbd)))) return 1;
//elm_config_engine_set("opengl_x11");
//elm_config_accel_preference_set("opengl");
elm_init(argc,argv);
elm_config_accel_preference_set("opengl_x11");
elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
win = elm_win_util_standard_add("glview simple", "GLView Simple");
elm_win_autodel_set(win, EINA_TRUE);
bx = elm_box_add(win);
evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(win, bx);
evas_object_show(bx);
gl = elm_glview_add(win);
evas_object_size_hint_align_set(gl, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_size_hint_weight_set(gl, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_glview_mode_set(gl, ELM_GLVIEW_ALPHA | ELM_GLVIEW_DEPTH);
elm_glview_resize_policy_set(gl, ELM_GLVIEW_RESIZE_POLICY_RECREATE);
elm_glview_render_policy_set(gl, ELM_GLVIEW_RENDER_POLICY_ALWAYS);
elm_glview_init_func_set(gl, _init_gl);
elm_glview_render_func_set(gl, _on_render);
elm_box_pack_end(bx, gl);
evas_object_show(gl);
elm_object_focus_set(gl, EINA_TRUE);
evas_object_resize(win, 320, 480);
evas_object_show(win);
mpv_handle *mpv = mpv_create();
if (!mpv)
die("context init failed");
ecore_idler_add(on_mpv,mpv);
// 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");
cb_data->glview = gl;
cb_data->mpv = mpv;
// Play this file.
const char *cmd[] = {"loadfile", "./video.ogv", NULL};
mpv_command_async(mpv,0, cmd);
// run the mainloop and process events and callbacks
elm_run();
// 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(cb_data->mpv_gl);
mpv_terminate_destroy(mpv);
elm_shutdown();
printf("properly terminated\n");
return 0;
}
`