mirror of https://github.com/mpv-player/mpv
vo_rpi: use EGL to render subtitles
Slightly faster than using the dispmanx mess (perhaps to a large amount due to the rather stupid C-only unoptimized ASS->RGBA blending code). Do this by reusing vo_opengl's subtitle renderer, and vo_opengl's RPI backend.
This commit is contained in:
parent
c36c596b7b
commit
58ba2a9087
|
@ -494,7 +494,7 @@ extern const struct mpgl_driver mpgl_driver_x11;
|
|||
extern const struct mpgl_driver mpgl_driver_x11egl;
|
||||
|
||||
static const struct backend backends[] = {
|
||||
#if HAVE_RPI_GLES
|
||||
#if HAVE_RPI
|
||||
{"rpi", mpgl_set_backend_rpi},
|
||||
#endif
|
||||
#if HAVE_GL_COCOA
|
||||
|
|
|
@ -553,6 +553,8 @@ static void sc_flush_cache(struct gl_shader_cache *sc)
|
|||
|
||||
void gl_sc_destroy(struct gl_shader_cache *sc)
|
||||
{
|
||||
if (!sc)
|
||||
return;
|
||||
gl_sc_reset(sc);
|
||||
sc_flush_cache(sc);
|
||||
talloc_free(sc);
|
||||
|
|
|
@ -38,35 +38,25 @@
|
|||
#include "vo.h"
|
||||
#include "video/mp_image.h"
|
||||
#include "sub/osd.h"
|
||||
#include "sub/img_convert.h"
|
||||
#include "gl_osd.h"
|
||||
|
||||
// In theory, the number of RGBA subbitmaps the OSD code could give us is
|
||||
// unlimited; but in practice there will be rarely many elements.
|
||||
#define MAX_OSD_ELEMS MP_SUB_BB_LIST_MAX
|
||||
|
||||
struct osd_elem {
|
||||
DISPMANX_RESOURCE_HANDLE_T resource;
|
||||
DISPMANX_ELEMENT_HANDLE_T element;
|
||||
};
|
||||
|
||||
struct osd_part {
|
||||
struct osd_elem elems[MAX_OSD_ELEMS];
|
||||
int num_elems;
|
||||
int change_id;
|
||||
bool needed;
|
||||
};
|
||||
#include "gl_rpi.h"
|
||||
|
||||
struct priv {
|
||||
DISPMANX_DISPLAY_HANDLE_T display;
|
||||
DISPMANX_ELEMENT_HANDLE_T window;
|
||||
DISPMANX_ELEMENT_HANDLE_T osd_overlay;
|
||||
DISPMANX_UPDATE_HANDLE_T update;
|
||||
uint32_t w, h;
|
||||
double display_fps;
|
||||
|
||||
struct osd_part osd_parts[MAX_OSD_PARTS];
|
||||
double osd_pts;
|
||||
struct mp_osd_res osd_res;
|
||||
|
||||
struct mp_egl_rpi egl;
|
||||
struct gl_shader_cache *sc;
|
||||
struct mpgl_osd *osd;
|
||||
|
||||
MMAL_COMPONENT_T *renderer;
|
||||
bool renderer_enabled;
|
||||
|
||||
|
@ -118,108 +108,38 @@ static size_t layout_buffer(struct mp_image *mpi, MMAL_BUFFER_HEADER_T *buffer,
|
|||
return size;
|
||||
}
|
||||
|
||||
static void wipe_osd_part(struct vo *vo, struct osd_part *part)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
for (int n = 0; n < part->num_elems; n++) {
|
||||
vc_dispmanx_element_remove(p->update, part->elems[n].element);
|
||||
vc_dispmanx_resource_delete(part->elems[n].resource);
|
||||
}
|
||||
part->num_elems = 0;
|
||||
part->change_id = -1;
|
||||
}
|
||||
|
||||
static void wipe_osd(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
for (int x = 0; x < MAX_OSD_PARTS; x++)
|
||||
wipe_osd_part(vo, &p->osd_parts[x]);
|
||||
}
|
||||
|
||||
static int add_element(struct vo *vo, struct osd_part *part, int index,
|
||||
struct sub_bitmap *sub)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
VC_IMAGE_TYPE_T format = VC_IMAGE_ARGB8888; // assuming RPI is always LE
|
||||
|
||||
struct osd_elem *elem = &part->elems[index];
|
||||
*elem = (struct osd_elem){0};
|
||||
|
||||
// I have no idea why stride must be passed in such a hacky way. It's not
|
||||
// documented. Other software does it too. Other software claims aligning
|
||||
// the width and "probably" the height is required too, but for me it works
|
||||
// just fine without on rpi2. (See Weston's rpi renderer.)
|
||||
elem->resource = vc_dispmanx_resource_create(format,
|
||||
sub->w | (sub->stride << 16),
|
||||
sub->h,
|
||||
&(int32_t){0});
|
||||
if (!elem->resource) {
|
||||
MP_ERR(vo, "Could not create %dx%d sub-bitmap\n", sub->w, sub->h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
VC_RECT_T rc = {.width = sub->w, .height = sub->h};
|
||||
vc_dispmanx_resource_write_data(elem->resource, format,
|
||||
sub->stride, sub->bitmap, &rc);
|
||||
VC_RECT_T src = {.width = sub->w << 16, .height = sub->h << 16};
|
||||
VC_RECT_T dst = {.x = sub->x, .y = sub->y, .width = sub->dw, .height = sub->dh};
|
||||
VC_DISPMANX_ALPHA_T alpha = {
|
||||
.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_PREMULT,
|
||||
.opacity = 0xFF,
|
||||
};
|
||||
elem->element = vc_dispmanx_element_add(p->update, p->display, p->osd_layer,
|
||||
&dst, elem->resource, &src,
|
||||
DISPMANX_PROTECTION_NONE,
|
||||
&alpha, 0, 0);
|
||||
if (!elem->element) {
|
||||
MP_ERR(vo, "Could not create sub-bitmap element\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void osd_draw_cb(void *ctx, struct sub_bitmaps *imgs)
|
||||
{
|
||||
struct vo *vo = ctx;
|
||||
struct priv *p = vo->priv;
|
||||
struct osd_part *part = &p->osd_parts[imgs->render_index];
|
||||
|
||||
part->needed = true;
|
||||
|
||||
if (imgs->change_id == part->change_id)
|
||||
return;
|
||||
|
||||
wipe_osd_part(vo, part);
|
||||
part->change_id = imgs->change_id;
|
||||
|
||||
for (int n = 0; n < imgs->num_parts; n++) {
|
||||
if (part->num_elems == MAX_OSD_ELEMS) {
|
||||
MP_ERR(vo, "Too many OSD elements.\n");
|
||||
break;
|
||||
}
|
||||
int index = part->num_elems++;
|
||||
if (add_element(vo, part, index, &imgs->parts[n]) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#define GLSL(x) gl_sc_add(p->sc, #x "\n");
|
||||
#define GLSLF(...) gl_sc_addf(p->sc, __VA_ARGS__)
|
||||
|
||||
static void update_osd(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
for (int x = 0; x < MAX_OSD_PARTS; x++)
|
||||
p->osd_parts[x].needed = false;
|
||||
mpgl_osd_generate(p->osd, p->osd_res, p->osd_pts, 0, 0);
|
||||
|
||||
static const bool formats[SUBBITMAP_COUNT] = {[SUBBITMAP_RGBA] = true};
|
||||
osd_draw(vo->osd, p->osd_res, p->osd_pts, 0, formats, osd_draw_cb, vo);
|
||||
|
||||
for (int x = 0; x < MAX_OSD_PARTS; x++) {
|
||||
struct osd_part *part = &p->osd_parts[x];
|
||||
if (!part->needed)
|
||||
wipe_osd_part(vo, part);
|
||||
for (int n = 0; n < MAX_OSD_PARTS; n++) {
|
||||
enum sub_bitmap_format fmt = mpgl_osd_get_part_format(p->osd, n);
|
||||
if (!fmt)
|
||||
continue;
|
||||
gl_sc_uniform_sampler(p->sc, "osdtex", GL_TEXTURE_2D, 0);
|
||||
switch (fmt) {
|
||||
case SUBBITMAP_RGBA: {
|
||||
GLSLF("// OSD (RGBA)\n");
|
||||
GLSL(vec4 color = texture(osdtex, texcoord).bgra;)
|
||||
break;
|
||||
}
|
||||
case SUBBITMAP_LIBASS: {
|
||||
GLSLF("// OSD (libass)\n");
|
||||
GLSL(vec4 color =
|
||||
vec4(ass_color.rgb, ass_color.a * texture(osdtex, texcoord).r);)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
gl_sc_set_vao(p->sc, mpgl_osd_get_vao(p->osd));
|
||||
gl_sc_gen_shader_and_reset(p->sc);
|
||||
mpgl_osd_draw_part(p->osd, p->w, -p->h, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,6 +169,25 @@ static void resize(struct vo *vo)
|
|||
MP_WARN(vo, "could not set video rectangle\n");
|
||||
}
|
||||
|
||||
static void destroy_overlays(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
if (p->window)
|
||||
vc_dispmanx_element_remove(p->update, p->window);
|
||||
p->window = 0;
|
||||
|
||||
mpgl_osd_destroy(p->osd);
|
||||
p->osd = NULL;
|
||||
gl_sc_destroy(p->sc);
|
||||
p->sc = NULL;
|
||||
mp_egl_rpi_destroy(&p->egl);
|
||||
|
||||
if (p->osd_overlay)
|
||||
vc_dispmanx_element_remove(p->update, p->osd_overlay);
|
||||
p->osd_overlay = 0;
|
||||
}
|
||||
|
||||
static int update_display_size(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
@ -267,9 +206,7 @@ static int update_display_size(struct vo *vo)
|
|||
|
||||
MP_VERBOSE(vo, "Display size: %dx%d\n", p->w, p->h);
|
||||
|
||||
if (p->window)
|
||||
vc_dispmanx_element_remove(p->update, p->window);
|
||||
p->window = 0;
|
||||
destroy_overlays(vo);
|
||||
|
||||
// Use the whole screen.
|
||||
VC_RECT_T dst = {.width = p->w, .height = p->h};
|
||||
|
@ -286,6 +223,27 @@ static int update_display_size(struct vo *vo)
|
|||
return -1;
|
||||
}
|
||||
|
||||
alpha = (VC_DISPMANX_ALPHA_T){
|
||||
.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE,
|
||||
.opacity = 0xFF,
|
||||
};
|
||||
p->osd_overlay = vc_dispmanx_element_add(p->update, p->display,
|
||||
p->background_layer + 2,
|
||||
&dst, 0, &src,
|
||||
DISPMANX_PROTECTION_NONE,
|
||||
&alpha, 0, 0);
|
||||
if (!p->osd_overlay) {
|
||||
MP_FATAL(vo, "Could not add DISPMANX element.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mp_egl_rpi_init(&p->egl, p->osd_overlay, p->w, p->h) < 0) {
|
||||
MP_FATAL(vo, "EGL/GLES initialization for OSD renderer failed.\n");
|
||||
return -1;
|
||||
}
|
||||
p->sc = gl_sc_create(p->egl.gl, vo->log, vo->global),
|
||||
p->osd = mpgl_osd_init(p->egl.gl, vo->log, vo->osd);
|
||||
|
||||
p->display_fps = 0;
|
||||
TV_GET_STATE_RESP_T tvstate;
|
||||
TV_DISPLAY_STATE_T tvstate_disp;
|
||||
|
@ -329,10 +287,8 @@ static void flip_page(struct vo *vo)
|
|||
p->next_image = NULL;
|
||||
|
||||
// For OSD
|
||||
if (!p->skip_osd) {
|
||||
vc_dispmanx_update_submit_sync(p->update);
|
||||
p->update = vc_dispmanx_update_start(10);
|
||||
}
|
||||
if (!p->skip_osd && p->egl.gl)
|
||||
eglSwapBuffers(p->egl.egl_display, p->egl.egl_surface);
|
||||
p->skip_osd = false;
|
||||
|
||||
if (mpi) {
|
||||
|
@ -375,8 +331,11 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
|
|||
// Redraw only if the OSD has meaningfully changed, which we assume it
|
||||
// hasn't when a frame is merely repeated for display sync.
|
||||
p->skip_osd = !frame->redraw && frame->repeat;
|
||||
if (!p->skip_osd)
|
||||
if (!p->skip_osd && p->egl.gl) {
|
||||
p->egl.gl->ClearColor(0, 0, 0, 0);
|
||||
p->egl.gl->Clear(GL_COLOR_BUFFER_BIT);
|
||||
update_osd(vo);
|
||||
}
|
||||
|
||||
p->display_synced = frame->display_synced;
|
||||
|
||||
|
@ -610,10 +569,7 @@ static void uninit(struct vo *vo)
|
|||
|
||||
talloc_free(p->next_image);
|
||||
|
||||
wipe_osd(vo);
|
||||
|
||||
if (p->window)
|
||||
vc_dispmanx_element_remove(p->update, p->window);
|
||||
destroy_overlays(vo);
|
||||
|
||||
if (p->update)
|
||||
vc_dispmanx_update_submit_sync(p->update);
|
||||
|
@ -642,6 +598,8 @@ static int preinit(struct vo *vo)
|
|||
p->video_layer = p->layer + 1;
|
||||
p->osd_layer = p->layer + 2;
|
||||
|
||||
p->egl.log = vo->log;
|
||||
|
||||
bcm_host_init();
|
||||
|
||||
if (mmal_vc_init()) {
|
||||
|
|
16
wscript
16
wscript
|
@ -676,7 +676,7 @@ video_output_features = [
|
|||
# including them (compensate with -isystem and -fgnu89-inline).
|
||||
'name': '--rpi',
|
||||
'desc': 'Raspberry Pi support',
|
||||
'func':
|
||||
'func': compose_checks(
|
||||
check_cc(cflags="-isystem/opt/vc/include/ "+
|
||||
"-isystem/opt/vc/include/interface/vcos/pthreads " +
|
||||
"-isystem/opt/vc/include/interface/vmcs_host/linux " +
|
||||
|
@ -684,23 +684,17 @@ video_output_features = [
|
|||
linkflags="-L/opt/vc/lib",
|
||||
header_name="bcm_host.h",
|
||||
lib=['mmal_core', 'mmal_util', 'mmal_vc_client', 'bcm_host']),
|
||||
}, {
|
||||
'name': '--rpi-gles',
|
||||
'desc': 'GLES on Raspberry Pi',
|
||||
'groups': [ 'gl' ],
|
||||
'deps': ['rpi'],
|
||||
# We still need all OpenGL symbols, because the vo_opengl code is
|
||||
# generic and supports anything from GLES2/OpenGL 2.1 to OpenGL 4 core.
|
||||
'func': compose_checks(
|
||||
# We still need all OpenGL symbols, because the vo_opengl code is
|
||||
# generic and supports anything from GLES2/OpenGL 2.1 to OpenGL 4 core.
|
||||
check_cc(lib="EGL"),
|
||||
check_cc(lib="GLESv2"),
|
||||
check_statement('GL/gl.h', '(void)GL_RGB32F'), # arbitrary OpenGL 3.0 symbol
|
||||
check_statement('GL/gl.h', '(void)GL_LUMINANCE16') # arbitrary OpenGL legacy-only symbol
|
||||
),
|
||||
),
|
||||
} , {
|
||||
'name': '--gl',
|
||||
'desc': 'OpenGL video outputs',
|
||||
'deps_any': [ 'gl-cocoa', 'gl-x11', 'gl-win32', 'gl-wayland', 'rpi-gles' ],
|
||||
'deps_any': [ 'gl-cocoa', 'gl-x11', 'gl-win32', 'gl-wayland', 'rpi' ],
|
||||
'func': check_true
|
||||
}
|
||||
]
|
||||
|
|
|
@ -333,7 +333,7 @@ def build(ctx):
|
|||
( "video/out/filter_kernels.c" ),
|
||||
( "video/out/gl_cocoa.c", "gl-cocoa" ),
|
||||
( "video/out/gl_common.c", "gl" ),
|
||||
( "video/out/gl_rpi.c", "rpi-gles" ),
|
||||
( "video/out/gl_rpi.c", "rpi" ),
|
||||
( "video/out/gl_hwdec.c", "gl" ),
|
||||
( "video/out/gl_hwdec_dxva2.c", "gl-win32" ),
|
||||
( "video/out/gl_hwdec_vaglx.c", "vaapi-glx" ),
|
||||
|
|
Loading…
Reference in New Issue