2015-03-29 13:12:11 +00:00
|
|
|
/*
|
|
|
|
* This file is part of mpv.
|
|
|
|
*
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
* mpv is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2015-03-29 13:12:11 +00:00
|
|
|
*
|
|
|
|
* mpv is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2015-03-29 13:12:11 +00:00
|
|
|
*
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2015-03-29 13:12:11 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <bcm_host.h>
|
|
|
|
#include <interface/mmal/mmal.h>
|
|
|
|
#include <interface/mmal/util/mmal_util.h>
|
|
|
|
#include <interface/mmal/util/mmal_default_components.h>
|
2015-04-13 16:33:06 +00:00
|
|
|
#include <interface/mmal/vc/mmal_vc_api.h>
|
2015-03-29 13:12:11 +00:00
|
|
|
|
2016-09-13 18:30:03 +00:00
|
|
|
#include <EGL/egl.h>
|
|
|
|
#include <EGL/eglext.h>
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
#include <libavutil/rational.h>
|
|
|
|
|
|
|
|
#include "common/common.h"
|
|
|
|
#include "common/msg.h"
|
2017-08-11 11:02:13 +00:00
|
|
|
#include "opengl/common.h"
|
2015-03-29 13:12:11 +00:00
|
|
|
#include "options/m_config.h"
|
2016-04-26 15:01:37 +00:00
|
|
|
#include "osdep/timer.h"
|
2015-03-29 13:12:11 +00:00
|
|
|
#include "vo.h"
|
2016-01-16 20:39:23 +00:00
|
|
|
#include "win_state.h"
|
2015-03-29 13:12:11 +00:00
|
|
|
#include "video/mp_image.h"
|
|
|
|
#include "sub/osd.h"
|
|
|
|
|
2017-08-11 11:02:13 +00:00
|
|
|
#include "opengl/ra_gl.h"
|
vo_opengl: refactor into vo_gpu
This is done in several steps:
1. refactor MPGLContext -> struct ra_ctx
2. move GL-specific stuff in vo_opengl into opengl/context.c
3. generalize context creation to support other APIs, and add --gpu-api
4. rename all of the --opengl- options that are no longer opengl-specific
5. move all of the stuff from opengl/* that isn't GL-specific into gpu/
(note: opengl/gl_utils.h became opengl/utils.h)
6. rename vo_opengl to vo_gpu
7. to handle window screenshots, the short-term approach was to just add
it to ra_swchain_fns. Long term (and for vulkan) this has to be moved to
ra itself (and vo_gpu altered to compensate), but this was a stop-gap
measure to prevent this commit from getting too big
8. move ra->fns->flush to ra_gl_ctx instead
9. some other minor changes that I've probably already forgotten
Note: This is one half of a major refactor, the other half of which is
provided by rossy's following commit. This commit enables support for
all linux platforms, while his version enables support for all non-linux
platforms.
Note 2: vo_opengl_cb.c also re-uses ra_gl_ctx so it benefits from the
--opengl- options like --opengl-early-flush, --opengl-finish etc. Should
be a strict superset of the old functionality.
Disclaimer: Since I have no way of compiling mpv on all platforms, some
of these ports were done blindly. Specifically, the blind ports included
context_mali_fbdev.c and context_rpi.c. Since they're both based on
egl_helpers, the port should have gone smoothly without any major
changes required. But if somebody complains about a compile error on
those platforms (assuming anybody actually uses them), you know where to
complain.
2017-09-14 06:04:55 +00:00
|
|
|
#include "gpu/video.h"
|
2016-09-13 18:30:03 +00:00
|
|
|
|
|
|
|
struct mp_egl_rpi {
|
|
|
|
struct mp_log *log;
|
|
|
|
struct GL *gl;
|
2017-08-11 11:02:13 +00:00
|
|
|
struct ra *ra;
|
2016-09-13 18:30:03 +00:00
|
|
|
EGLDisplay egl_display;
|
|
|
|
EGLConfig egl_config;
|
|
|
|
EGLContext egl_context;
|
|
|
|
EGLSurface egl_surface;
|
|
|
|
// yep, the API keeps a pointer to it
|
|
|
|
EGL_DISPMANX_WINDOW_T egl_window;
|
|
|
|
};
|
2015-03-29 13:12:11 +00:00
|
|
|
|
|
|
|
struct priv {
|
|
|
|
DISPMANX_DISPLAY_HANDLE_T display;
|
|
|
|
DISPMANX_ELEMENT_HANDLE_T window;
|
2015-08-18 20:35:33 +00:00
|
|
|
DISPMANX_ELEMENT_HANDLE_T osd_overlay;
|
2015-03-29 13:12:11 +00:00
|
|
|
DISPMANX_UPDATE_HANDLE_T update;
|
|
|
|
uint32_t w, h;
|
2016-01-16 20:39:23 +00:00
|
|
|
uint32_t x, y;
|
2015-07-27 19:09:24 +00:00
|
|
|
double display_fps;
|
2015-03-29 13:12:11 +00:00
|
|
|
|
|
|
|
double osd_pts;
|
|
|
|
struct mp_osd_res osd_res;
|
2021-08-11 07:01:10 +00:00
|
|
|
struct m_config_cache *opts_cache;
|
2015-03-29 13:12:11 +00:00
|
|
|
|
2015-08-18 20:35:33 +00:00
|
|
|
struct mp_egl_rpi egl;
|
2017-08-11 11:02:13 +00:00
|
|
|
struct gl_video *gl_video;
|
2015-08-18 20:35:33 +00:00
|
|
|
struct mpgl_osd *osd;
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
MMAL_COMPONENT_T *renderer;
|
|
|
|
bool renderer_enabled;
|
|
|
|
|
2015-08-13 10:23:18 +00:00
|
|
|
bool display_synced, skip_osd;
|
2015-03-29 13:12:11 +00:00
|
|
|
struct mp_image *next_image;
|
|
|
|
|
|
|
|
// for RAM input
|
|
|
|
MMAL_POOL_T *swpool;
|
|
|
|
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_mutex_t display_mutex;
|
|
|
|
pthread_cond_t display_cond;
|
2015-08-10 16:44:52 +00:00
|
|
|
int64_t vsync_counter;
|
2016-04-26 14:03:42 +00:00
|
|
|
bool reload_display;
|
2015-08-10 16:44:52 +00:00
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
int background_layer;
|
|
|
|
int video_layer;
|
|
|
|
int osd_layer;
|
|
|
|
|
|
|
|
int display_nr;
|
|
|
|
int layer;
|
2015-08-20 17:07:18 +00:00
|
|
|
int background;
|
2015-11-25 21:06:17 +00:00
|
|
|
int enable_osd;
|
2015-03-29 13:12:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Magic alignments (in pixels) expected by the MMAL internals.
|
|
|
|
#define ALIGN_W 32
|
|
|
|
#define ALIGN_H 16
|
|
|
|
|
2016-04-26 14:03:42 +00:00
|
|
|
static void recreate_renderer(struct vo *vo);
|
|
|
|
|
2016-09-13 18:30:03 +00:00
|
|
|
static void *get_proc_address(const GLubyte *name)
|
|
|
|
{
|
|
|
|
void *p = eglGetProcAddress(name);
|
|
|
|
// EGL 1.4 (supported by the RPI firmware) does not necessarily return
|
|
|
|
// function pointers for core functions.
|
|
|
|
if (!p) {
|
2020-04-19 12:21:53 +00:00
|
|
|
void *h = dlopen("/opt/vc/lib/libbrcmGLESv2.so", RTLD_LAZY);
|
2016-09-13 18:30:03 +00:00
|
|
|
if (h) {
|
|
|
|
p = dlsym(h, name);
|
|
|
|
dlclose(h);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static EGLConfig select_fb_config_egl(struct mp_egl_rpi *p)
|
|
|
|
{
|
|
|
|
EGLint attributes[] = {
|
|
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
|
|
EGL_RED_SIZE, 8,
|
|
|
|
EGL_GREEN_SIZE, 8,
|
|
|
|
EGL_BLUE_SIZE, 8,
|
|
|
|
EGL_DEPTH_SIZE, 0,
|
|
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
EGLint config_count;
|
|
|
|
EGLConfig config;
|
|
|
|
|
|
|
|
eglChooseConfig(p->egl_display, attributes, &config, 1, &config_count);
|
|
|
|
|
|
|
|
if (!config_count) {
|
|
|
|
MP_FATAL(p, "Could find EGL configuration!\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mp_egl_rpi_destroy(struct mp_egl_rpi *p)
|
|
|
|
{
|
|
|
|
if (p->egl_display) {
|
|
|
|
eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
|
|
EGL_NO_CONTEXT);
|
|
|
|
}
|
|
|
|
if (p->egl_surface)
|
|
|
|
eglDestroySurface(p->egl_display, p->egl_surface);
|
|
|
|
if (p->egl_context)
|
|
|
|
eglDestroyContext(p->egl_display, p->egl_context);
|
|
|
|
p->egl_context = EGL_NO_CONTEXT;
|
|
|
|
eglReleaseThread();
|
|
|
|
p->egl_display = EGL_NO_DISPLAY;
|
|
|
|
talloc_free(p->gl);
|
|
|
|
p->gl = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mp_egl_rpi_init(struct mp_egl_rpi *p, DISPMANX_ELEMENT_HANDLE_T window,
|
|
|
|
int w, int h)
|
|
|
|
{
|
|
|
|
p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
|
|
if (!eglInitialize(p->egl_display, NULL, NULL)) {
|
|
|
|
MP_FATAL(p, "EGL failed to initialize.\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
eglBindAPI(EGL_OPENGL_ES_API);
|
|
|
|
|
|
|
|
EGLConfig config = select_fb_config_egl(p);
|
|
|
|
if (!config)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
p->egl_window = (EGL_DISPMANX_WINDOW_T){
|
|
|
|
.element = window,
|
|
|
|
.width = w,
|
|
|
|
.height = h,
|
|
|
|
};
|
|
|
|
p->egl_surface = eglCreateWindowSurface(p->egl_display, config,
|
|
|
|
&p->egl_window, NULL);
|
|
|
|
|
|
|
|
if (p->egl_surface == EGL_NO_SURFACE) {
|
|
|
|
MP_FATAL(p, "Could not create EGL surface!\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
EGLint context_attributes[] = {
|
|
|
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
p->egl_context = eglCreateContext(p->egl_display, config,
|
|
|
|
EGL_NO_CONTEXT, context_attributes);
|
|
|
|
|
|
|
|
if (p->egl_context == EGL_NO_CONTEXT) {
|
|
|
|
MP_FATAL(p, "Could not create EGL context!\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
|
|
|
|
p->egl_context);
|
|
|
|
|
|
|
|
p->gl = talloc_zero(NULL, struct GL);
|
|
|
|
|
|
|
|
const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS);
|
|
|
|
mpgl_load_functions(p->gl, get_proc_address, exts, p->log);
|
|
|
|
|
|
|
|
if (!p->gl->version && !p->gl->es)
|
|
|
|
goto fail;
|
|
|
|
|
2017-08-11 11:02:13 +00:00
|
|
|
p->ra = ra_create_gl(p->gl, p->log);
|
|
|
|
if (!p->ra)
|
|
|
|
goto fail;
|
|
|
|
|
2016-09-13 18:30:03 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
mp_egl_rpi_destroy(p);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
// Make mpi point to buffer, assuming MMAL_ENCODING_I420.
|
|
|
|
// buffer can be NULL.
|
|
|
|
// Return the required buffer space.
|
|
|
|
static size_t layout_buffer(struct mp_image *mpi, MMAL_BUFFER_HEADER_T *buffer,
|
|
|
|
struct mp_image_params *params)
|
|
|
|
{
|
|
|
|
assert(params->imgfmt == IMGFMT_420P);
|
|
|
|
mp_image_set_params(mpi, params);
|
|
|
|
int w = MP_ALIGN_UP(params->w, ALIGN_W);
|
|
|
|
int h = MP_ALIGN_UP(params->h, ALIGN_H);
|
|
|
|
uint8_t *cur = buffer ? buffer->data : NULL;
|
|
|
|
size_t size = 0;
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
int div = i ? 2 : 1;
|
|
|
|
mpi->planes[i] = cur;
|
|
|
|
mpi->stride[i] = w / div;
|
|
|
|
size_t plane_size = h / div * mpi->stride[i];
|
|
|
|
if (cur)
|
|
|
|
cur += plane_size;
|
|
|
|
size += plane_size;
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2015-08-18 20:35:33 +00:00
|
|
|
static void update_osd(struct vo *vo)
|
2015-03-29 13:12:11 +00:00
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
2015-11-25 21:06:17 +00:00
|
|
|
if (!p->enable_osd)
|
|
|
|
return;
|
2015-03-29 13:12:11 +00:00
|
|
|
|
2017-08-11 11:02:13 +00:00
|
|
|
if (!gl_video_check_osd_change(p->gl_video, &p->osd_res, p->osd_pts)) {
|
2015-08-18 20:59:22 +00:00
|
|
|
p->skip_osd = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-25 21:06:50 +00:00
|
|
|
MP_STATS(vo, "start rpi_osd");
|
|
|
|
|
2017-08-15 17:12:39 +00:00
|
|
|
struct vo_frame frame = {0};
|
2017-10-17 07:28:00 +00:00
|
|
|
struct ra_fbo target = {
|
2017-08-11 11:02:13 +00:00
|
|
|
.tex = ra_create_wrapped_fb(p->egl.ra, 0, p->osd_res.w, p->osd_res.h),
|
|
|
|
.flip = true,
|
|
|
|
};
|
2017-08-15 17:12:39 +00:00
|
|
|
gl_video_set_osd_pts(p->gl_video, p->osd_pts);
|
2018-02-08 00:55:31 +00:00
|
|
|
gl_video_render_frame(p->gl_video, &frame, target, RENDER_FRAME_DEF);
|
2017-08-11 11:02:13 +00:00
|
|
|
ra_tex_free(p->egl.ra, &target.tex);
|
2015-11-25 21:06:50 +00:00
|
|
|
|
|
|
|
MP_STATS(vo, "stop rpi_osd");
|
2015-03-29 13:12:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void resize(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
MMAL_PORT_T *input = p->renderer->input[0];
|
|
|
|
|
|
|
|
struct mp_rect src, dst;
|
|
|
|
|
|
|
|
vo_get_src_dst_rects(vo, &src, &dst, &p->osd_res);
|
|
|
|
|
2016-01-05 13:34:45 +00:00
|
|
|
int rotate[] = {MMAL_DISPLAY_ROT0,
|
|
|
|
MMAL_DISPLAY_ROT90,
|
|
|
|
MMAL_DISPLAY_ROT180,
|
|
|
|
MMAL_DISPLAY_ROT270};
|
|
|
|
|
|
|
|
|
2015-11-25 21:06:58 +00:00
|
|
|
int src_w = src.x1 - src.x0, src_h = src.y1 - src.y0,
|
|
|
|
dst_w = dst.x1 - dst.x0, dst_h = dst.y1 - dst.y0;
|
2016-01-05 13:09:48 +00:00
|
|
|
int p_x, p_y;
|
|
|
|
av_reduce(&p_x, &p_y, dst_w * src_h, src_w * dst_h, 16000);
|
2015-03-29 13:12:11 +00:00
|
|
|
MMAL_DISPLAYREGION_T dr = {
|
|
|
|
.hdr = { .id = MMAL_PARAMETER_DISPLAYREGION,
|
|
|
|
.size = sizeof(MMAL_DISPLAYREGION_T), },
|
2015-11-25 21:06:58 +00:00
|
|
|
.src_rect = { .x = src.x0, .y = src.y0, .width = src_w, .height = src_h },
|
2016-01-16 20:39:23 +00:00
|
|
|
.dest_rect = { .x = dst.x0 + p->x, .y = dst.y0 + p->y,
|
|
|
|
.width = dst_w, .height = dst_h },
|
2015-03-29 13:12:11 +00:00
|
|
|
.layer = p->video_layer,
|
|
|
|
.display_num = p->display_nr,
|
2016-01-05 13:09:48 +00:00
|
|
|
.pixel_x = p_x,
|
|
|
|
.pixel_y = p_y,
|
2016-01-05 13:34:45 +00:00
|
|
|
.transform = rotate[vo->params ? vo->params->rotate / 90 : 0],
|
2016-01-16 20:39:23 +00:00
|
|
|
.fullscreen = vo->opts->fullscreen,
|
2015-03-29 13:12:11 +00:00
|
|
|
.set = MMAL_DISPLAY_SET_SRC_RECT | MMAL_DISPLAY_SET_DEST_RECT |
|
2015-11-25 21:06:58 +00:00
|
|
|
MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM |
|
2016-01-16 20:39:23 +00:00
|
|
|
MMAL_DISPLAY_SET_PIXEL | MMAL_DISPLAY_SET_TRANSFORM |
|
|
|
|
MMAL_DISPLAY_SET_FULLSCREEN,
|
2015-03-29 13:12:11 +00:00
|
|
|
};
|
|
|
|
|
2016-01-05 13:34:45 +00:00
|
|
|
if (vo->params && (vo->params->rotate % 180) == 90) {
|
|
|
|
MPSWAP(int, dr.src_rect.x, dr.src_rect.y);
|
|
|
|
MPSWAP(int, dr.src_rect.width, dr.src_rect.height);
|
|
|
|
}
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
if (mmal_port_parameter_set(input, &dr.hdr))
|
|
|
|
MP_WARN(vo, "could not set video rectangle\n");
|
2017-08-15 17:12:39 +00:00
|
|
|
|
|
|
|
if (p->gl_video)
|
|
|
|
gl_video_resize(p->gl_video, &src, &dst, &p->osd_res);
|
2015-03-29 13:12:11 +00:00
|
|
|
}
|
|
|
|
|
2015-08-18 20:35:33 +00:00
|
|
|
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;
|
|
|
|
|
2017-08-11 11:02:13 +00:00
|
|
|
gl_video_uninit(p->gl_video);
|
|
|
|
p->gl_video = NULL;
|
|
|
|
ra_free(&p->egl.ra);
|
2015-08-18 20:35:33 +00:00
|
|
|
mp_egl_rpi_destroy(&p->egl);
|
|
|
|
|
|
|
|
if (p->osd_overlay)
|
|
|
|
vc_dispmanx_element_remove(p->update, p->osd_overlay);
|
|
|
|
p->osd_overlay = 0;
|
|
|
|
}
|
|
|
|
|
2015-04-30 19:21:09 +00:00
|
|
|
static int update_display_size(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
uint32_t n_w = 0, n_h = 0;
|
|
|
|
if (graphics_get_display_size(0, &n_w, &n_h) < 0) {
|
|
|
|
MP_FATAL(vo, "Could not get display size.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->w == n_w && p->h == n_h)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
p->w = n_w;
|
|
|
|
p->h = n_h;
|
|
|
|
|
|
|
|
MP_VERBOSE(vo, "Display size: %dx%d\n", p->w, p->h);
|
|
|
|
|
2016-01-16 20:39:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int create_overlays(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
2015-08-18 20:35:33 +00:00
|
|
|
destroy_overlays(vo);
|
2015-04-30 19:21:09 +00:00
|
|
|
|
2016-04-26 14:03:42 +00:00
|
|
|
if (!p->display)
|
|
|
|
return -1;
|
|
|
|
|
2016-04-26 11:59:06 +00:00
|
|
|
if (vo->opts->fullscreen && p->background) {
|
|
|
|
// Use the whole screen.
|
|
|
|
VC_RECT_T dst = {.width = p->w, .height = p->h};
|
|
|
|
VC_RECT_T src = {.width = 1 << 16, .height = 1 << 16};
|
|
|
|
VC_DISPMANX_ALPHA_T alpha = {
|
|
|
|
.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
|
|
|
|
.opacity = 0xFF,
|
|
|
|
};
|
2015-08-20 17:07:18 +00:00
|
|
|
|
|
|
|
p->window = vc_dispmanx_element_add(p->update, p->display,
|
|
|
|
p->background_layer,
|
|
|
|
&dst, 0, &src,
|
|
|
|
DISPMANX_PROTECTION_NONE,
|
|
|
|
&alpha, 0, 0);
|
|
|
|
if (!p->window) {
|
|
|
|
MP_FATAL(vo, "Could not add DISPMANX element.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-04-30 19:21:09 +00:00
|
|
|
}
|
|
|
|
|
2015-11-25 21:06:17 +00:00
|
|
|
if (p->enable_osd) {
|
2016-01-16 20:39:23 +00:00
|
|
|
VC_RECT_T dst = {.x = p->x, .y = p->y,
|
|
|
|
.width = p->osd_res.w, .height = p->osd_res.h};
|
|
|
|
VC_RECT_T src = {.width = p->osd_res.w << 16, .height = p->osd_res.h << 16};
|
|
|
|
VC_DISPMANX_ALPHA_T alpha = {
|
2015-11-25 21:06:17 +00:00
|
|
|
.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE,
|
|
|
|
.opacity = 0xFF,
|
|
|
|
};
|
|
|
|
p->osd_overlay = vc_dispmanx_element_add(p->update, p->display,
|
|
|
|
p->osd_layer,
|
|
|
|
&dst, 0, &src,
|
|
|
|
DISPMANX_PROTECTION_NONE,
|
|
|
|
&alpha, 0, 0);
|
|
|
|
if (!p->osd_overlay) {
|
|
|
|
MP_FATAL(vo, "Could not add DISPMANX element.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-08-18 20:35:33 +00:00
|
|
|
|
2016-01-16 20:39:23 +00:00
|
|
|
if (mp_egl_rpi_init(&p->egl, p->osd_overlay,
|
|
|
|
p->osd_res.w, p->osd_res.h) < 0)
|
|
|
|
{
|
2015-11-25 21:06:17 +00:00
|
|
|
MP_FATAL(vo, "EGL/GLES initialization for OSD renderer failed.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2017-08-11 11:02:13 +00:00
|
|
|
p->gl_video = gl_video_init(p->egl.ra, vo->log, vo->global);
|
2017-08-15 17:12:39 +00:00
|
|
|
gl_video_set_clear_color(p->gl_video, (struct m_color){.a = 0});
|
2017-08-11 11:02:13 +00:00
|
|
|
gl_video_set_osd_source(p->gl_video, vo->osd);
|
2015-08-18 20:35:33 +00:00
|
|
|
}
|
|
|
|
|
2015-07-27 19:09:24 +00:00
|
|
|
p->display_fps = 0;
|
|
|
|
TV_GET_STATE_RESP_T tvstate;
|
|
|
|
TV_DISPLAY_STATE_T tvstate_disp;
|
|
|
|
if (!vc_tv_get_state(&tvstate) && !vc_tv_get_display_state(&tvstate_disp)) {
|
|
|
|
if (tvstate_disp.state & (VC_HDMI_HDMI | VC_HDMI_DVI)) {
|
|
|
|
p->display_fps = tvstate_disp.display.hdmi.frame_rate;
|
|
|
|
|
|
|
|
HDMI_PROPERTY_PARAM_T param = {
|
|
|
|
.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE,
|
|
|
|
};
|
|
|
|
if (!vc_tv_hdmi_get_property(¶m) &&
|
|
|
|
param.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC)
|
|
|
|
p->display_fps = p->display_fps / 1.001;
|
|
|
|
} else {
|
|
|
|
p->display_fps = tvstate_disp.display.sdtv.frame_rate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-15 17:12:39 +00:00
|
|
|
resize(vo);
|
|
|
|
|
2015-07-27 19:09:24 +00:00
|
|
|
vo_event(vo, VO_EVENT_WIN_STATE);
|
|
|
|
|
2015-04-30 19:21:09 +00:00
|
|
|
vc_dispmanx_update_submit_sync(p->update);
|
|
|
|
p->update = vc_dispmanx_update_start(10);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-16 20:39:23 +00:00
|
|
|
static int set_geometry(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
if (vo->opts->fullscreen) {
|
|
|
|
vo->dwidth = p->w;
|
|
|
|
vo->dheight = p->h;
|
|
|
|
p->x = p->y = 0;
|
|
|
|
} else {
|
|
|
|
struct vo_win_geometry geo;
|
|
|
|
struct mp_rect screenrc = {0, 0, p->w, p->h};
|
|
|
|
|
|
|
|
vo_calc_window_geometry(vo, &screenrc, &geo);
|
|
|
|
vo_apply_window_geometry(vo, &geo);
|
|
|
|
|
|
|
|
p->x = geo.win.x0;
|
|
|
|
p->y = geo.win.y0;
|
|
|
|
}
|
|
|
|
|
|
|
|
resize(vo);
|
|
|
|
|
|
|
|
if (create_overlays(vo) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-10 16:44:52 +00:00
|
|
|
static void wait_next_vsync(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_mutex_lock(&p->display_mutex);
|
2016-04-26 15:01:37 +00:00
|
|
|
struct timespec end = mp_rel_time_to_timespec(0.050);
|
2015-08-10 16:44:52 +00:00
|
|
|
int64_t old = p->vsync_counter;
|
2016-04-26 15:01:37 +00:00
|
|
|
while (old == p->vsync_counter && !p->reload_display) {
|
2016-04-26 15:12:01 +00:00
|
|
|
if (pthread_cond_timedwait(&p->display_cond, &p->display_mutex, &end))
|
2016-04-26 15:01:37 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_mutex_unlock(&p->display_mutex);
|
2015-08-10 16:44:52 +00:00
|
|
|
}
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
static void flip_page(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
2016-04-26 14:03:42 +00:00
|
|
|
|
|
|
|
if (!p->renderer_enabled)
|
|
|
|
return;
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
struct mp_image *mpi = p->next_image;
|
|
|
|
p->next_image = NULL;
|
|
|
|
|
|
|
|
// For OSD
|
2015-08-18 20:35:33 +00:00
|
|
|
if (!p->skip_osd && p->egl.gl)
|
|
|
|
eglSwapBuffers(p->egl.egl_display, p->egl.egl_surface);
|
2015-08-13 10:23:18 +00:00
|
|
|
p->skip_osd = false;
|
2015-03-29 13:12:11 +00:00
|
|
|
|
|
|
|
if (mpi) {
|
|
|
|
MMAL_PORT_T *input = p->renderer->input[0];
|
|
|
|
MMAL_BUFFER_HEADER_T *ref = (void *)mpi->planes[3];
|
|
|
|
|
|
|
|
// Assume this field is free for use by us.
|
|
|
|
ref->user_data = mpi;
|
|
|
|
|
|
|
|
if (mmal_port_send_buffer(input, ref)) {
|
|
|
|
MP_ERR(vo, "could not queue picture!\n");
|
|
|
|
talloc_free(mpi);
|
|
|
|
}
|
|
|
|
}
|
2015-08-10 16:44:52 +00:00
|
|
|
|
|
|
|
if (p->display_synced)
|
|
|
|
wait_next_vsync(vo);
|
2015-03-29 13:12:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void free_mmal_buffer(void *arg)
|
|
|
|
{
|
|
|
|
MMAL_BUFFER_HEADER_T *buffer = arg;
|
|
|
|
mmal_buffer_header_release(buffer);
|
|
|
|
}
|
|
|
|
|
2015-08-10 16:44:52 +00:00
|
|
|
static void draw_frame(struct vo *vo, struct vo_frame *frame)
|
2015-03-29 13:12:11 +00:00
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
2016-04-26 14:03:42 +00:00
|
|
|
if (!p->renderer_enabled)
|
|
|
|
return;
|
|
|
|
|
2015-08-10 16:44:52 +00:00
|
|
|
mp_image_t *mpi = NULL;
|
|
|
|
if (!frame->redraw && !frame->repeat)
|
|
|
|
mpi = mp_image_new_ref(frame->current);
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
talloc_free(p->next_image);
|
|
|
|
p->next_image = NULL;
|
|
|
|
|
2015-08-10 16:44:52 +00:00
|
|
|
if (mpi)
|
|
|
|
p->osd_pts = mpi->pts;
|
|
|
|
|
|
|
|
// Redraw only if the OSD has meaningfully changed, which we assume it
|
|
|
|
// hasn't when a frame is merely repeated for display sync.
|
2015-08-13 10:23:18 +00:00
|
|
|
p->skip_osd = !frame->redraw && frame->repeat;
|
2015-08-18 20:59:22 +00:00
|
|
|
|
|
|
|
if (!p->skip_osd && p->egl.gl)
|
2015-08-10 16:44:52 +00:00
|
|
|
update_osd(vo);
|
|
|
|
|
|
|
|
p->display_synced = frame->display_synced;
|
2015-03-29 13:12:11 +00:00
|
|
|
|
2015-08-14 11:19:17 +00:00
|
|
|
if (mpi && mpi->imgfmt != IMGFMT_MMAL) {
|
2015-03-29 13:12:11 +00:00
|
|
|
MMAL_BUFFER_HEADER_T *buffer = mmal_queue_wait(p->swpool->queue);
|
|
|
|
if (!buffer) {
|
|
|
|
talloc_free(mpi);
|
|
|
|
MP_ERR(vo, "Can't allocate buffer.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mmal_buffer_header_reset(buffer);
|
|
|
|
|
2016-04-25 09:28:49 +00:00
|
|
|
struct mp_image *new_ref = mp_image_new_custom_ref(NULL, buffer,
|
2015-03-29 13:12:11 +00:00
|
|
|
free_mmal_buffer);
|
|
|
|
if (!new_ref) {
|
|
|
|
mmal_buffer_header_release(buffer);
|
|
|
|
talloc_free(mpi);
|
|
|
|
MP_ERR(vo, "Out of memory.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mp_image_setfmt(new_ref, IMGFMT_MMAL);
|
|
|
|
new_ref->planes[3] = (void *)buffer;
|
|
|
|
|
|
|
|
struct mp_image dmpi = {0};
|
|
|
|
buffer->length = layout_buffer(&dmpi, buffer, vo->params);
|
|
|
|
mp_image_copy(&dmpi, mpi);
|
|
|
|
|
|
|
|
talloc_free(mpi);
|
|
|
|
mpi = new_ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->next_image = mpi;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int query_format(struct vo *vo, int format)
|
|
|
|
{
|
|
|
|
return format == IMGFMT_MMAL || format == IMGFMT_420P;
|
|
|
|
}
|
|
|
|
|
|
|
|
static MMAL_FOURCC_T map_csp(enum mp_csp csp)
|
|
|
|
{
|
|
|
|
switch (csp) {
|
|
|
|
case MP_CSP_BT_601: return MMAL_COLOR_SPACE_ITUR_BT601;
|
|
|
|
case MP_CSP_BT_709: return MMAL_COLOR_SPACE_ITUR_BT709;
|
|
|
|
case MP_CSP_SMPTE_240M: return MMAL_COLOR_SPACE_SMPTE240M;
|
|
|
|
default: return MMAL_COLOR_SPACE_UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
|
|
{
|
|
|
|
mmal_buffer_header_release(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
|
|
{
|
|
|
|
struct mp_image *mpi = buffer->user_data;
|
|
|
|
talloc_free(mpi);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void disable_renderer(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
if (p->renderer_enabled) {
|
|
|
|
mmal_port_disable(p->renderer->control);
|
|
|
|
mmal_port_disable(p->renderer->input[0]);
|
|
|
|
|
|
|
|
mmal_port_flush(p->renderer->control);
|
|
|
|
mmal_port_flush(p->renderer->input[0]);
|
|
|
|
|
|
|
|
mmal_component_disable(p->renderer);
|
|
|
|
}
|
|
|
|
mmal_pool_destroy(p->swpool);
|
|
|
|
p->swpool = NULL;
|
|
|
|
p->renderer_enabled = false;
|
|
|
|
}
|
|
|
|
|
2015-10-03 16:20:16 +00:00
|
|
|
static int reconfig(struct vo *vo, struct mp_image_params *params)
|
2015-03-29 13:12:11 +00:00
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
MMAL_PORT_T *input = p->renderer->input[0];
|
|
|
|
bool opaque = params->imgfmt == IMGFMT_MMAL;
|
|
|
|
|
2016-04-26 14:03:42 +00:00
|
|
|
if (!p->display)
|
|
|
|
return -1;
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
disable_renderer(vo);
|
|
|
|
|
|
|
|
input->format->encoding = opaque ? MMAL_ENCODING_OPAQUE : MMAL_ENCODING_I420;
|
|
|
|
input->format->es->video.width = MP_ALIGN_UP(params->w, ALIGN_W);
|
|
|
|
input->format->es->video.height = MP_ALIGN_UP(params->h, ALIGN_H);
|
|
|
|
input->format->es->video.crop = (MMAL_RECT_T){0, 0, params->w, params->h};
|
2015-12-26 18:38:38 +00:00
|
|
|
input->format->es->video.par = (MMAL_RATIONAL_T){params->p_w, params->p_h};
|
2016-06-29 07:16:13 +00:00
|
|
|
input->format->es->video.color_space = map_csp(params->color.space);
|
2015-03-29 13:12:11 +00:00
|
|
|
|
|
|
|
if (mmal_port_format_commit(input))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
input->buffer_num = MPMAX(input->buffer_num_min,
|
|
|
|
input->buffer_num_recommended) + 3;
|
|
|
|
input->buffer_size = MPMAX(input->buffer_size_min,
|
|
|
|
input->buffer_size_recommended);
|
|
|
|
|
|
|
|
if (!opaque) {
|
|
|
|
size_t size = layout_buffer(&(struct mp_image){0}, NULL, params);
|
|
|
|
if (input->buffer_size != size) {
|
|
|
|
MP_FATAL(vo, "We disagree with MMAL about buffer sizes.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->swpool = mmal_pool_create(input->buffer_num, input->buffer_size);
|
|
|
|
if (!p->swpool) {
|
|
|
|
MP_FATAL(vo, "Could not allocate buffer pool.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-16 20:39:23 +00:00
|
|
|
if (set_geometry(vo) < 0)
|
|
|
|
return -1;
|
2015-03-29 13:12:11 +00:00
|
|
|
|
|
|
|
p->renderer_enabled = true;
|
|
|
|
|
|
|
|
if (mmal_port_enable(p->renderer->control, control_port_cb))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (mmal_port_enable(input, input_port_cb))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (mmal_component_enable(p->renderer)) {
|
|
|
|
MP_FATAL(vo, "Failed to enable video renderer.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-08-15 17:12:39 +00:00
|
|
|
resize(vo);
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct mp_image *take_screenshot(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
2016-04-26 14:03:42 +00:00
|
|
|
if (!p->display)
|
|
|
|
return NULL;
|
|
|
|
|
2015-04-07 09:43:03 +00:00
|
|
|
struct mp_image *img = mp_image_alloc(IMGFMT_BGR0, p->w, p->h);
|
2015-03-29 13:12:11 +00:00
|
|
|
if (!img)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
DISPMANX_RESOURCE_HANDLE_T resource =
|
|
|
|
vc_dispmanx_resource_create(VC_IMAGE_ARGB8888,
|
|
|
|
img->w | ((img->w * 4) << 16), img->h,
|
|
|
|
&(int32_t){0});
|
|
|
|
if (!resource)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if (vc_dispmanx_snapshot(p->display, resource, 0))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
VC_RECT_T rc = {.width = img->w, .height = img->h};
|
|
|
|
if (vc_dispmanx_resource_read_data(resource, &rc, img->planes[0], img->stride[0]))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
vc_dispmanx_resource_delete(resource);
|
|
|
|
return img;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
vc_dispmanx_resource_delete(resource);
|
|
|
|
talloc_free(img);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-08-11 07:01:10 +00:00
|
|
|
static void set_fullscreen(struct vo *vo) {
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
if (p->renderer_enabled)
|
|
|
|
set_geometry(vo);
|
|
|
|
vo->want_redraw = true;
|
|
|
|
}
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
static int control(struct vo *vo, uint32_t request, void *data)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
switch (request) {
|
2021-08-11 07:01:10 +00:00
|
|
|
case VOCTRL_VO_OPTS_CHANGED: {
|
|
|
|
void *opt;
|
|
|
|
while (m_config_cache_get_next_changed(p->opts_cache, &opt)) {
|
|
|
|
struct mp_vo_opts *opts = p->opts_cache->opts;
|
|
|
|
if (&opts->fullscreen == opt)
|
|
|
|
set_fullscreen(vo);
|
|
|
|
}
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2015-03-29 13:12:11 +00:00
|
|
|
case VOCTRL_SET_PANSCAN:
|
|
|
|
if (p->renderer_enabled)
|
|
|
|
resize(vo);
|
2015-08-18 20:59:22 +00:00
|
|
|
vo->want_redraw = true;
|
2015-03-29 13:12:11 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_REDRAW_FRAME:
|
|
|
|
update_osd(vo);
|
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_SCREENSHOT_WIN:
|
|
|
|
*(struct mp_image **)data = take_screenshot(vo);
|
|
|
|
return VO_TRUE;
|
2016-04-26 14:03:42 +00:00
|
|
|
case VOCTRL_CHECK_EVENTS: {
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_mutex_lock(&p->display_mutex);
|
2016-04-26 14:03:42 +00:00
|
|
|
bool reload_required = p->reload_display;
|
|
|
|
p->reload_display = false;
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_mutex_unlock(&p->display_mutex);
|
2016-04-26 14:03:42 +00:00
|
|
|
if (reload_required)
|
|
|
|
recreate_renderer(vo);
|
2015-04-30 19:33:54 +00:00
|
|
|
return VO_TRUE;
|
2016-04-26 14:03:42 +00:00
|
|
|
}
|
2015-07-27 19:09:24 +00:00
|
|
|
case VOCTRL_GET_DISPLAY_FPS:
|
|
|
|
*(double *)data = p->display_fps;
|
|
|
|
return VO_TRUE;
|
2021-04-12 17:51:41 +00:00
|
|
|
case VOCTRL_GET_DISPLAY_RES:
|
2021-06-08 15:28:10 +00:00
|
|
|
((int *)data)[0] = p->w;
|
|
|
|
((int *)data)[1] = p->h;
|
2021-04-12 17:51:41 +00:00
|
|
|
return VO_TRUE;
|
2015-03-29 13:12:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return VO_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2015-04-30 19:33:54 +00:00
|
|
|
static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1,
|
|
|
|
uint32_t param2)
|
|
|
|
{
|
|
|
|
struct vo *vo = callback_data;
|
|
|
|
struct priv *p = vo->priv;
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_mutex_lock(&p->display_mutex);
|
2016-04-26 14:03:42 +00:00
|
|
|
p->reload_display = true;
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_cond_signal(&p->display_cond);
|
|
|
|
pthread_mutex_unlock(&p->display_mutex);
|
2015-04-30 19:33:54 +00:00
|
|
|
vo_wakeup(vo);
|
|
|
|
}
|
|
|
|
|
2015-08-10 16:44:52 +00:00
|
|
|
static void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *arg)
|
|
|
|
{
|
|
|
|
struct vo *vo = arg;
|
|
|
|
struct priv *p = vo->priv;
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_mutex_lock(&p->display_mutex);
|
2015-08-10 16:44:52 +00:00
|
|
|
p->vsync_counter += 1;
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_cond_signal(&p->display_cond);
|
|
|
|
pthread_mutex_unlock(&p->display_mutex);
|
2015-08-10 16:44:52 +00:00
|
|
|
}
|
|
|
|
|
2016-04-26 14:03:42 +00:00
|
|
|
static void destroy_dispmanx(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
disable_renderer(vo);
|
|
|
|
destroy_overlays(vo);
|
|
|
|
|
2021-09-06 06:05:12 +00:00
|
|
|
if (p->update)
|
|
|
|
vc_dispmanx_update_submit_sync(p->update);
|
|
|
|
p->update = 0;
|
|
|
|
|
2016-04-26 14:03:42 +00:00
|
|
|
if (p->display) {
|
|
|
|
vc_dispmanx_vsync_callback(p->display, NULL, NULL);
|
|
|
|
vc_dispmanx_display_close(p->display);
|
|
|
|
}
|
|
|
|
p->display = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int recreate_dispmanx(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
p->display = vc_dispmanx_display_open(p->display_nr);
|
|
|
|
p->update = vc_dispmanx_update_start(0);
|
|
|
|
if (!p->display || !p->update) {
|
|
|
|
MP_FATAL(vo, "Could not get DISPMANX objects.\n");
|
|
|
|
if (p->display)
|
|
|
|
vc_dispmanx_display_close(p->display);
|
|
|
|
p->display = 0;
|
|
|
|
p->update = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
update_display_size(vo);
|
|
|
|
|
|
|
|
vc_dispmanx_vsync_callback(p->display, vsync_callback, vo);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void recreate_renderer(struct vo *vo)
|
|
|
|
{
|
|
|
|
MP_WARN(vo, "Recreating renderer after display change.\n");
|
|
|
|
|
|
|
|
destroy_dispmanx(vo);
|
|
|
|
recreate_dispmanx(vo);
|
|
|
|
|
|
|
|
if (vo->params) {
|
|
|
|
if (reconfig(vo, vo->params) < 0)
|
|
|
|
MP_FATAL(vo, "Recreation failed.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
static void uninit(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
2015-04-30 19:33:54 +00:00
|
|
|
vc_tv_unregister_callback_full(tv_callback, vo);
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
talloc_free(p->next_image);
|
|
|
|
|
2016-04-26 14:03:42 +00:00
|
|
|
destroy_dispmanx(vo);
|
2015-03-29 13:12:11 +00:00
|
|
|
|
2016-04-26 14:03:42 +00:00
|
|
|
if (p->renderer)
|
2015-03-29 13:12:11 +00:00
|
|
|
mmal_component_release(p->renderer);
|
2015-04-13 16:33:06 +00:00
|
|
|
|
|
|
|
mmal_vc_deinit();
|
2015-08-10 16:44:52 +00:00
|
|
|
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_cond_destroy(&p->display_cond);
|
|
|
|
pthread_mutex_destroy(&p->display_mutex);
|
2015-03-29 13:12:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int preinit(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
p->background_layer = p->layer;
|
|
|
|
p->video_layer = p->layer + 1;
|
|
|
|
p->osd_layer = p->layer + 2;
|
|
|
|
|
2015-08-18 20:35:33 +00:00
|
|
|
p->egl.log = vo->log;
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
bcm_host_init();
|
|
|
|
|
2015-04-13 16:33:06 +00:00
|
|
|
if (mmal_vc_init()) {
|
|
|
|
MP_FATAL(vo, "Could not initialize MMAL.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-04-26 15:12:01 +00:00
|
|
|
pthread_mutex_init(&p->display_mutex, NULL);
|
|
|
|
pthread_cond_init(&p->display_cond, NULL);
|
2016-04-26 14:03:42 +00:00
|
|
|
|
2021-08-11 07:01:10 +00:00
|
|
|
p->opts_cache = m_config_cache_alloc(p, vo->global, &vo_sub_opts);
|
|
|
|
|
2016-04-26 14:03:42 +00:00
|
|
|
if (recreate_dispmanx(vo) < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if (update_display_size(vo) < 0)
|
2015-03-29 13:12:11 +00:00
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &p->renderer))
|
|
|
|
{
|
|
|
|
MP_FATAL(vo, "Could not create MMAL renderer.\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-04-30 19:33:54 +00:00
|
|
|
vc_tv_register_callback(tv_callback, vo);
|
|
|
|
|
2015-03-29 13:12:11 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
uninit(vo);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define OPT_BASE_STRUCT struct priv
|
|
|
|
static const struct m_option options[] = {
|
options: change option macros and all option declarations
Change all OPT_* macros such that they don't define the entire m_option
initializer, and instead expand only to a part of it, which sets certain
fields. This requires changing almost every option declaration, because
they all use these macros. A declaration now always starts with
{"name", ...
followed by designated initializers only (possibly wrapped in macros).
The OPT_* macros now initialize the .offset and .type fields only,
sometimes also .priv and others.
I think this change makes the option macros less tricky. The old code
had to stuff everything into macro arguments (and attempted to allow
setting arbitrary fields by letting the user pass designated
initializers in the vararg parts). Some of this was made messy due to
C99 and C11 not allowing 0-sized varargs with ',' removal. It's also
possible that this change is pointless, other than cosmetic preferences.
Not too happy about some things. For example, the OPT_CHOICE()
indentation I applied looks a bit ugly.
Much of this change was done with regex search&replace, but some places
required manual editing. In particular, code in "obscure" areas (which I
didn't include in compilation) might be broken now.
In wayland_common.c the author of some option declarations confused the
flags parameter with the default value (though the default value was
also properly set below). I fixed this with this change.
2020-03-14 20:28:01 +00:00
|
|
|
{"display", OPT_INT(display_nr)},
|
|
|
|
{"layer", OPT_INT(layer), OPTDEF_INT(-10)},
|
|
|
|
{"background", OPT_FLAG(background)},
|
|
|
|
{"osd", OPT_FLAG(enable_osd), OPTDEF_INT(1)},
|
2015-03-29 13:12:11 +00:00
|
|
|
{0},
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct vo_driver video_out_rpi = {
|
|
|
|
.description = "Raspberry Pi (MMAL)",
|
|
|
|
.name = "rpi",
|
2016-01-05 13:34:45 +00:00
|
|
|
.caps = VO_CAP_ROTATE90,
|
2015-03-29 13:12:11 +00:00
|
|
|
.preinit = preinit,
|
|
|
|
.query_format = query_format,
|
|
|
|
.reconfig = reconfig,
|
|
|
|
.control = control,
|
2015-08-10 16:44:52 +00:00
|
|
|
.draw_frame = draw_frame,
|
2015-03-29 13:12:11 +00:00
|
|
|
.flip_page = flip_page,
|
|
|
|
.uninit = uninit,
|
|
|
|
.priv_size = sizeof(struct priv),
|
|
|
|
.options = options,
|
2016-11-25 20:00:39 +00:00
|
|
|
.options_prefix = "rpi",
|
2015-03-29 13:12:11 +00:00
|
|
|
};
|