Files
plezy/linux/runner/mpv/mpv_texture.cc
T

183 lines
5.1 KiB
C++

#include "mpv_texture.h"
#include <epoxy/gl.h>
struct _MpvTexture {
FlTextureGL parent_instance;
mpv::MpvPlayer* player; // not owned
FlTextureRegistrar* registrar; // not owned
FlView* view; // not owned, for querying allocation size
GLuint fbo;
GLuint texture;
int32_t width;
int32_t height;
};
G_DEFINE_TYPE(MpvTexture, mpv_texture, fl_texture_gl_get_type())
static void ensure_fbo(MpvTexture* self, int32_t w, int32_t h) {
if (self->fbo != 0 && self->width == w && self->height == h) {
return;
}
// Delete old resources.
if (self->fbo != 0) {
glDeleteFramebuffers(1, &self->fbo);
self->fbo = 0;
}
if (self->texture != 0) {
glDeleteTextures(1, &self->texture);
self->texture = 0;
}
self->width = w;
self->height = h;
glGenTextures(1, &self->texture);
glBindTexture(GL_TEXTURE_2D, self->texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenFramebuffers(1, &self->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
self->texture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
static gboolean mpv_texture_populate(FlTextureGL* gl_texture,
uint32_t* target,
uint32_t* name,
uint32_t* width,
uint32_t* height,
GError** error) {
MpvTexture* self = MPV_TEXTURE(gl_texture);
if (!self->player) {
return FALSE;
}
// Lazily create the mpv render context on first populate() call,
// since Flutter's GL context is current here.
if (!self->player->HasRenderContext()) {
if (!self->player->InitRenderContext()) {
g_set_error(error, g_quark_from_static_string("mpv"), 0,
"Failed to create mpv render context");
return FALSE;
}
}
// Determine target size from the FlView widget allocation.
GtkAllocation alloc;
gtk_widget_get_allocation(GTK_WIDGET(self->view), &alloc);
int scale = gtk_widget_get_scale_factor(GTK_WIDGET(self->view));
int32_t w = alloc.width * scale;
int32_t h = alloc.height * scale;
if (w <= 0 || h <= 0) {
return FALSE;
}
ensure_fbo(self, w, h);
// Save GL state that mpv may clobber.
GLint prev_viewport[4];
GLint prev_scissor_box[4];
GLboolean prev_blend, prev_scissor_test;
GLint prev_blend_src, prev_blend_dst;
GLint prev_fbo;
glGetIntegerv(GL_VIEWPORT, prev_viewport);
glGetIntegerv(GL_SCISSOR_BOX, prev_scissor_box);
glGetBooleanv(GL_BLEND, &prev_blend);
glGetBooleanv(GL_SCISSOR_TEST, &prev_scissor_test);
glGetIntegerv(GL_BLEND_SRC_ALPHA, &prev_blend_src);
glGetIntegerv(GL_BLEND_DST_ALPHA, &prev_blend_dst);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
// Render mpv into our FBO.
glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
glViewport(0, 0, w, h);
self->player->ClearRedrawFlag();
self->player->Render(w, h, static_cast<int>(self->fbo));
// Restore GL state.
glViewport(prev_viewport[0], prev_viewport[1], prev_viewport[2],
prev_viewport[3]);
glScissor(prev_scissor_box[0], prev_scissor_box[1], prev_scissor_box[2],
prev_scissor_box[3]);
if (prev_blend)
glEnable(GL_BLEND);
else
glDisable(GL_BLEND);
if (prev_scissor_test)
glEnable(GL_SCISSOR_TEST);
else
glDisable(GL_SCISSOR_TEST);
glBlendFunc(prev_blend_src, prev_blend_dst);
glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
*target = GL_TEXTURE_2D;
*name = self->texture;
*width = static_cast<uint32_t>(w);
*height = static_cast<uint32_t>(h);
return TRUE;
}
static void mpv_texture_class_init(MpvTextureClass* klass) {
FL_TEXTURE_GL_CLASS(klass)->populate = mpv_texture_populate;
}
static void mpv_texture_init(MpvTexture* self) {
self->player = nullptr;
self->registrar = nullptr;
self->view = nullptr;
self->fbo = 0;
self->texture = 0;
self->width = 0;
self->height = 0;
}
MpvTexture* mpv_texture_new(mpv::MpvPlayer* player,
FlTextureRegistrar* registrar,
FlView* view) {
MpvTexture* self = MPV_TEXTURE(g_object_new(MPV_TEXTURE_TYPE, nullptr));
self->player = player;
self->registrar = registrar;
self->view = view;
return self;
}
void mpv_texture_mark_frame_available(MpvTexture* self) {
if (self && self->registrar) {
fl_texture_registrar_mark_texture_frame_available(
self->registrar, FL_TEXTURE(self));
}
}
void mpv_texture_dispose(MpvTexture* self) {
if (!self) return;
if (self->fbo != 0) {
glDeleteFramebuffers(1, &self->fbo);
self->fbo = 0;
}
if (self->texture != 0) {
glDeleteTextures(1, &self->texture);
self->texture = 0;
}
self->player = nullptr;
self->registrar = nullptr;
self->view = nullptr;
}
int64_t mpv_texture_get_id(MpvTexture* self) {
return fl_texture_get_id(FL_TEXTURE(self));
}