summaryrefslogtreecommitdiffstats
path: root/video/out/vo_rpi.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-08-18 22:35:33 +0200
committerwm4 <wm4@nowhere>2015-08-18 23:01:09 +0200
commit58ba2a9087dfa6bf6a81177c77f86e01acd33286 (patch)
tree9142d608569dbf4056a6a2cb3ab82a1190a137ee /video/out/vo_rpi.c
parentc36c596b7bc4700d05eaacdd229e8ebd3e312018 (diff)
downloadmpv-58ba2a9087dfa6bf6a81177c77f86e01acd33286.tar.bz2
mpv-58ba2a9087dfa6bf6a81177c77f86e01acd33286.tar.xz
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.
Diffstat (limited to 'video/out/vo_rpi.c')
-rw-r--r--video/out/vo_rpi.c206
1 files changed, 82 insertions, 124 deletions
diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c
index 615a5a8997..95627d4073 100644
--- a/video/out/vo_rpi.c
+++ b/video/out/vo_rpi.c
@@ -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;
-}
+#define GLSL(x) gl_sc_add(p->sc, #x "\n");
+#define GLSLF(...) gl_sc_addf(p->sc, __VA_ARGS__)
-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)
+static void update_osd(struct vo *vo)
{
- 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");
+ mpgl_osd_generate(p->osd, p->osd_res, p->osd_pts, 0, 0);
+
+ 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;
}
- int index = part->num_elems++;
- if (add_element(vo, part, index, &imgs->parts[n]) < 0)
+ case SUBBITMAP_LIBASS: {
+ GLSLF("// OSD (libass)\n");
+ GLSL(vec4 color =
+ vec4(ass_color.rgb, ass_color.a * texture(osdtex, texcoord).r);)
break;
- }
-}
-
-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;
-
- 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);
+ }
+ 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()) {