vo_rpi: attempt to survive display mode changes

Recreate all dispmanx objects after mode changes signalled by the TV
callback. This is needed since dispmanx objects are marked as invalid
and cease working.

One important point is that the vsync callbacks will stop coming when
this happens, so restoring the callback is important.

Note that the MMAL renderer itself does not get trashed by the firmware
on such events, but we completely reconfigure it anyway when it happens.
This commit is contained in:
wm4 2016-04-26 16:03:42 +02:00
parent dfa88271a2
commit 74e3d11f21
1 changed files with 90 additions and 38 deletions

View File

@ -30,8 +30,6 @@
#include <libavutil/rational.h>
#include "osdep/atomics.h"
#include "common/common.h"
#include "common/msg.h"
#include "options/m_config.h"
@ -69,11 +67,10 @@ struct priv {
// for RAM input
MMAL_POOL_T *swpool;
atomic_bool update_display;
pthread_mutex_t vsync_mutex;
pthread_cond_t vsync_cond;
int64_t vsync_counter;
bool reload_display;
int background_layer;
int video_layer;
@ -89,6 +86,8 @@ struct priv {
#define ALIGN_W 32
#define ALIGN_H 16
static void recreate_renderer(struct vo *vo);
// Make mpi point to buffer, assuming MMAL_ENCODING_I420.
// buffer can be NULL.
// Return the required buffer space.
@ -255,6 +254,9 @@ static int create_overlays(struct vo *vo)
struct priv *p = vo->priv;
destroy_overlays(vo);
if (!p->display)
return -1;
if (vo->opts->fullscreen && p->background) {
// Use the whole screen.
VC_RECT_T dst = {.width = p->w, .height = p->h};
@ -362,7 +364,7 @@ static void wait_next_vsync(struct vo *vo)
struct priv *p = vo->priv;
pthread_mutex_lock(&p->vsync_mutex);
int64_t old = p->vsync_counter;
while (old == p->vsync_counter)
while (old == p->vsync_counter && !p->reload_display)
pthread_cond_wait(&p->vsync_cond, &p->vsync_mutex);
pthread_mutex_unlock(&p->vsync_mutex);
}
@ -370,6 +372,10 @@ static void wait_next_vsync(struct vo *vo)
static void flip_page(struct vo *vo)
{
struct priv *p = vo->priv;
if (!p->renderer_enabled)
return;
struct mp_image *mpi = p->next_image;
p->next_image = NULL;
@ -405,6 +411,9 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
{
struct priv *p = vo->priv;
if (!p->renderer_enabled)
return;
mp_image_t *mpi = NULL;
if (!frame->redraw && !frame->repeat)
mpi = mp_image_new_ref(frame->current);
@ -506,6 +515,9 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
MMAL_PORT_T *input = p->renderer->input[0];
bool opaque = params->imgfmt == IMGFMT_MMAL;
if (!p->display)
return -1;
disable_renderer(vo);
input->format->encoding = opaque ? MMAL_ENCODING_OPAQUE : MMAL_ENCODING_I420;
@ -560,6 +572,9 @@ static struct mp_image *take_screenshot(struct vo *vo)
{
struct priv *p = vo->priv;
if (!p->display)
return NULL;
struct mp_image *img = mp_image_alloc(IMGFMT_BGR0, p->w, p->h);
if (!img)
return NULL;
@ -612,14 +627,15 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_SCREENSHOT_WIN:
*(struct mp_image **)data = take_screenshot(vo);
return VO_TRUE;
case VOCTRL_CHECK_EVENTS:
if (atomic_load(&p->update_display)) {
atomic_store(&p->update_display, false);
update_display_size(vo);
if (p->renderer_enabled)
set_geometry(vo);
}
case VOCTRL_CHECK_EVENTS: {
pthread_mutex_lock(&p->vsync_mutex);
bool reload_required = p->reload_display;
p->reload_display = false;
pthread_mutex_unlock(&p->vsync_mutex);
if (reload_required)
recreate_renderer(vo);
return VO_TRUE;
}
case VOCTRL_GET_DISPLAY_FPS:
*(double *)data = p->display_fps;
return VO_TRUE;
@ -633,7 +649,10 @@ static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1,
{
struct vo *vo = callback_data;
struct priv *p = vo->priv;
atomic_store(&p->update_display, true);
pthread_mutex_lock(&p->vsync_mutex);
p->reload_display = true;
pthread_cond_signal(&p->vsync_cond);
pthread_mutex_unlock(&p->vsync_mutex);
vo_wakeup(vo);
}
@ -647,6 +666,55 @@ static void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *arg)
pthread_mutex_unlock(&p->vsync_mutex);
}
static void destroy_dispmanx(struct vo *vo)
{
struct priv *p = vo->priv;
disable_renderer(vo);
destroy_overlays(vo);
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");
}
}
static void uninit(struct vo *vo)
{
struct priv *p = vo->priv;
@ -655,20 +723,10 @@ static void uninit(struct vo *vo)
talloc_free(p->next_image);
destroy_overlays(vo);
destroy_dispmanx(vo);
if (p->update)
vc_dispmanx_update_submit_sync(p->update);
if (p->renderer) {
disable_renderer(vo);
if (p->renderer)
mmal_component_release(p->renderer);
}
if (p->display) {
vc_dispmanx_vsync_callback(p->display, NULL, NULL);
vc_dispmanx_display_close(p->display);
}
mmal_vc_deinit();
@ -693,12 +751,14 @@ static int preinit(struct vo *vo)
return -1;
}
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");
pthread_mutex_init(&p->vsync_mutex, NULL);
pthread_cond_init(&p->vsync_cond, NULL);
if (recreate_dispmanx(vo) < 0)
goto fail;
if (update_display_size(vo) < 0)
goto fail;
}
if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &p->renderer))
{
@ -706,16 +766,8 @@ static int preinit(struct vo *vo)
goto fail;
}
if (update_display_size(vo) < 0)
goto fail;
vc_tv_register_callback(tv_callback, vo);
pthread_mutex_init(&p->vsync_mutex, NULL);
pthread_cond_init(&p->vsync_cond, NULL);
vc_dispmanx_vsync_callback(p->display, vsync_callback, vo);
return 0;
fail: