DisplayManagerRPI::DisplayManagerRPI(QObject* parent) : DisplayManager(parent) { connect(this, &DisplayManagerRPI::onTvChange, this, &DisplayManagerRPI::handleTvChange, Qt::QueuedConnection); vc_tv_register_callback(&tv_callback, (void *)this); }
static int start_monitor( void ) { if ( vcos_event_create( &quit_event, "tvservice" ) != VCOS_SUCCESS ) { LOG_ERR( "Failed to create quit event" ); return -1; } // Handle the INT and TERM signals so we can quit signal( SIGINT, control_c ); signal( SIGTERM, control_c ); vc_tv_register_callback( &tvservice_callback, NULL ); return 0; }
int tvserviceInit() { if (initialized) return 0; VCHI_INSTANCE_T vchi_instance; VCHI_CONNECTION_T *vchi_connections; // initialise bcm_host initializeBCM(); // initialise vcos/vchi initializeVCOS(); if (vchi_initialise(&vchi_instance) != VCHIQ_SUCCESS) { logInfo(LOG_TVSERVICE, "failed to open vchiq instance\n"); return -2; } // create a vchi connection if ( vchi_connect( NULL, 0, vchi_instance ) != 0) { logInfo(LOG_TVSERVICE, "failed to connect to VCHI\n"); return -3; } // connect to tvservice if ( vc_vchi_tv_init( vchi_instance, &vchi_connections, 1) != 0) { logInfo(LOG_TVSERVICE, "failed to connect to tvservice\n"); return -4; } vc_tv_register_callback(&tvserviceCallback, NULL); tvstate = malloc(sizeof(TV_GET_STATE_RESP_T)); vc_tv_get_state(tvstate); logInfo(LOG_TVSERVICE, "tvstate: %dx%d, %d fps, %s\n", tvstate->width, tvstate->height, tvstate->frame_rate, tvstate->scan_mode ? "interlaced" : "progressive"); if (vc_tv_hdmi_set_spd("1st Setup", "piMythClient", HDMI_SPD_TYPE_PMP) != 0) { logInfo(LOG_TVSERVICE, "failed to vc_tv_hdmi_set_spd\n"); } initialized = 1; return 0; }
GLUSvoid _glusDestroyNativeWindow() { if (_nativeWindowCreated) { DISPMANX_UPDATE_HANDLE_T dispmanUpdate; dispmanUpdate = vc_dispmanx_update_start(0); vc_dispmanx_element_remove(dispmanUpdate, _nativeWindow.element); vc_dispmanx_update_submit_sync(dispmanUpdate); _nativeWindowCreated = GLUS_FALSE; } memset(&_nativeWindow, 0, sizeof(_nativeWindow)); if (_nativeDisplay) { vc_dispmanx_display_close(_nativeDisplay); _nativeDisplay = 0; } if (_fullscreen) { vc_tv_register_callback(resizeDone, 0); if (vc_tv_hdmi_power_on_preferred() == 0) { waitResizeDone(); } vc_tv_unregister_callback(resizeDone); _fullscreen = GLUS_FALSE; } SDL_ShowCursor(SDL_ENABLE); SDL_Quit(); bcm_host_deinit(); }
static int Open(vlc_object_t *object) { vout_display_t *vd = (vout_display_t *)object; vout_display_sys_t *sys; uint32_t buffer_pitch, buffer_height; vout_display_place_t place; MMAL_DISPLAYREGION_T display_region; uint32_t offsets[3]; MMAL_STATUS_T status; int ret = VLC_SUCCESS; unsigned i; if (vout_display_IsWindowed(vd)) return VLC_EGENERIC; sys = calloc(1, sizeof(struct vout_display_sys_t)); if (!sys) return VLC_ENOMEM; vd->sys = sys; sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); bcm_host_init(); vd->info.has_hide_mouse = true; sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE; status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component); if (status != MMAL_SUCCESS) { msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)", MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status)); ret = VLC_EGENERIC; goto out; } sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; status = mmal_port_enable(sys->component->control, control_port_cb); if (status != MMAL_SUCCESS) { msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)", sys->component->control->name, status, mmal_status_to_string(status)); ret = VLC_EGENERIC; goto out; } sys->input = sys->component->input[0]; sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd; if (sys->opaque) { sys->input->format->encoding = MMAL_ENCODING_OPAQUE; sys->i_planes = 1; sys->buffer_size = sys->input->buffer_size_recommended; } else { sys->input->format->encoding = MMAL_ENCODING_I420; vd->fmt.i_chroma = VLC_CODEC_I420; buffer_pitch = align(vd->fmt.i_width, 32); buffer_height = align(vd->fmt.i_height, 16); sys->i_planes = 3; sys->buffer_size = 3 * buffer_pitch * buffer_height / 2; } sys->input->format->es->video.width = vd->fmt.i_width; sys->input->format->es->video.height = vd->fmt.i_height; sys->input->format->es->video.crop.x = 0; sys->input->format->es->video.crop.y = 0; sys->input->format->es->video.crop.width = vd->fmt.i_width; sys->input->format->es->video.crop.height = vd->fmt.i_height; sys->input->format->es->video.par.num = vd->source.i_sar_num; sys->input->format->es->video.par.den = vd->source.i_sar_den; status = mmal_port_format_commit(sys->input); if (status != MMAL_SUCCESS) { msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)", sys->input->name, status, mmal_status_to_string(status)); ret = VLC_EGENERIC; goto out; } sys->input->buffer_size = sys->input->buffer_size_recommended; vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T); display_region.fullscreen = MMAL_FALSE; display_region.src_rect.x = vd->fmt.i_x_offset; display_region.src_rect.y = vd->fmt.i_y_offset; display_region.src_rect.width = vd->fmt.i_visible_width; display_region.src_rect.height = vd->fmt.i_visible_height; display_region.dest_rect.x = place.x; display_region.dest_rect.y = place.y; display_region.dest_rect.width = place.width; display_region.dest_rect.height = place.height; display_region.layer = sys->layer; display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT | MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER; status = mmal_port_parameter_set(sys->input, &display_region.hdr); if (status != MMAL_SUCCESS) { msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", status, mmal_status_to_string(status)); ret = VLC_EGENERIC; goto out; } offsets[0] = 0; for (i = 0; i < sys->i_planes; ++i) { sys->planes[i].i_lines = buffer_height; sys->planes[i].i_pitch = buffer_pitch; sys->planes[i].i_visible_lines = vd->fmt.i_visible_height; sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width; if (i > 0) { offsets[i] = offsets[i - 1] + sys->planes[i - 1].i_pitch * sys->planes[i - 1].i_lines; sys->planes[i].i_lines /= 2; sys->planes[i].i_pitch /= 2; sys->planes[i].i_visible_lines /= 2; sys->planes[i].i_visible_pitch /= 2; } sys->planes[i].p_pixels = (uint8_t *)offsets[i]; } vlc_mutex_init(&sys->buffer_mutex); vlc_cond_init(&sys->buffer_cond); vlc_mutex_init(&sys->manage_mutex); vd->pool = vd_pool; vd->prepare = vd_prepare; vd->display = vd_display; vd->control = vd_control; vd->manage = vd_manage; vc_tv_register_callback(tvservice_cb, vd); if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) { vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height); } else { sys->display_width = vd->cfg->display.width; sys->display_height = vd->cfg->display.height; } sys->dmx_handle = vc_dispmanx_display_open(0); vd->info.subpicture_chromas = subpicture_chromas; out: if (ret != VLC_SUCCESS) Close(object); return ret; }
static bool recreate_dispmanx(struct ra_ctx *ctx) { struct priv *p = ctx->priv; int display_nr = 0; int layer = 0; MP_VERBOSE(ctx, "Recreating DISPMANX state...\n"); destroy_dispmanx(ctx); p->display = vc_dispmanx_display_open(display_nr); p->update = vc_dispmanx_update_start(0); if (!p->display || !p->update) { MP_FATAL(ctx, "Could not get DISPMANX objects.\n"); goto fail; } uint32_t dispw, disph; if (graphics_get_display_size(0, &dispw, &disph) < 0) { MP_FATAL(ctx, "Could not get display size.\n"); goto fail; } p->w = dispw; p->h = disph; if (ctx->vo->opts->fullscreen) { 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(ctx->vo, &screenrc, &geo); mp_rect_intersection(&geo.win, &screenrc); p->x = geo.win.x0; p->y = geo.win.y0; p->w = geo.win.x1 - geo.win.x0; p->h = geo.win.y1 - geo.win.y0; } // dispmanx is like a neanderthal version of Wayland - you can add an // overlay any place on the screen. VC_RECT_T dst = {.x = p->x, .y = p->y, .width = p->w, .height = p->h}; VC_RECT_T src = {.width = p->w << 16, .height = p->h << 16}; VC_DISPMANX_ALPHA_T alpha = { .flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE, .opacity = 0xFF, }; p->window = vc_dispmanx_element_add(p->update, p->display, layer, &dst, 0, &src, DISPMANX_PROTECTION_NONE, &alpha, 0, 0); if (!p->window) { MP_FATAL(ctx, "Could not add DISPMANX element.\n"); goto fail; } vc_dispmanx_update_submit_sync(p->update); p->update = vc_dispmanx_update_start(0); p->egl_window = (EGL_DISPMANX_WINDOW_T){ .element = p->window, .width = p->w, .height = p->h, }; p->egl_surface = eglCreateWindowSurface(p->egl_display, p->egl_config, &p->egl_window, NULL); if (p->egl_surface == EGL_NO_SURFACE) { MP_FATAL(ctx, "Could not create EGL surface!\n"); goto fail; } if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface, p->egl_context)) { MP_FATAL(ctx, "Failed to set context!\n"); goto fail; } 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; } } p->win_params[0] = display_nr; p->win_params[1] = layer; p->win_params[2] = p->x; p->win_params[3] = p->y; ctx->vo->dwidth = p->w; ctx->vo->dheight = p->h; ra_gl_ctx_resize(ctx->swapchain, p->w, p->h, 0); ctx->vo->want_redraw = true; vo_event(ctx->vo, VO_EVENT_WIN_STATE); return true; fail: destroy_dispmanx(ctx); return false; } static void rpi_swap_buffers(struct ra_ctx *ctx) { struct priv *p = ctx->priv; eglSwapBuffers(p->egl_display, p->egl_surface); } static bool rpi_init(struct ra_ctx *ctx) { struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); bcm_host_init(); vc_tv_register_callback(tv_callback, ctx); p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (!eglInitialize(p->egl_display, NULL, NULL)) { MP_FATAL(ctx, "EGL failed to initialize.\n"); goto fail; } if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context, &p->egl_config)) goto fail; if (recreate_dispmanx(ctx) < 0) goto fail; mpegl_load_functions(&p->gl, ctx->log); struct ra_gl_ctx_params params = { .swap_buffers = rpi_swap_buffers, .native_display_type = "MPV_RPI_WINDOW", .native_display = p->win_params, }; if (!ra_gl_ctx_init(ctx, &p->gl, params)) goto fail; return true; fail: rpi_uninit(ctx); return false; } static bool rpi_reconfig(struct ra_ctx *ctx) { return recreate_dispmanx(ctx); } static struct mp_image *take_screenshot(struct ra_ctx *ctx) { struct priv *p = ctx->priv; if (!p->display) return NULL; struct mp_image *img = mp_image_alloc(IMGFMT_BGR0, p->w, p->h); 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; } static int rpi_control(struct ra_ctx *ctx, int *events, int request, void *arg) { struct priv *p = ctx->priv; switch (request) { case VOCTRL_SCREENSHOT_WIN: *(struct mp_image **)arg = take_screenshot(ctx); return VO_TRUE; case VOCTRL_FULLSCREEN: recreate_dispmanx(ctx); return VO_TRUE; case VOCTRL_CHECK_EVENTS: if (atomic_fetch_and(&p->reload_display, 0)) { MP_WARN(ctx, "Recovering from display mode switch...\n"); recreate_dispmanx(ctx); } return VO_TRUE; case VOCTRL_GET_DISPLAY_FPS: *(double *)arg = p->display_fps; return VO_TRUE; } return VO_NOTIMPL; } const struct ra_ctx_fns ra_ctx_rpi = { .type = "opengl", .name = "rpi", .reconfig = rpi_reconfig, .control = rpi_control, .init = rpi_init, .uninit = rpi_uninit, };
EGLNativeWindowType _glusCreateNativeWindowType(const char* title, const GLUSint width, const GLUSint height, const GLUSboolean fullscreen, const GLUSboolean noResize) { const SDL_VideoInfo* videoInfo; // DISPMANX_UPDATE_HANDLE_T dispmanUpdate; DISPMANX_ELEMENT_HANDLE_T dispmanElement; VC_RECT_T dstRect; VC_RECT_T srcRect; VC_DISPMANX_ALPHA_T dispmanAlpha; int32_t success; int32_t windowWidth; int32_t windowHeight; glusLogPrint(GLUS_LOG_INFO, "Parameters 'title' and 'noResize' are not used"); // Initialize graphics system bcm_host_init(); // Set fullscreen, if wanted if (fullscreen) { const uint32_t MAX_SUPPORTED_MODES = 128; HDMI_RES_GROUP_T group = HDMI_RES_GROUP_DMT; TV_SUPPORTED_MODE_NEW_T supportedModes[MAX_SUPPORTED_MODES]; int32_t i, numberSupportedModes; numberSupportedModes = vc_tv_hdmi_get_supported_modes_new(group, supportedModes, MAX_SUPPORTED_MODES, 0, 0); for (i = 0; i < numberSupportedModes; i++) { if (supportedModes[i].width == (uint32_t)width && supportedModes[i].height == (uint32_t)height && supportedModes[i].frame_rate >= 60) { break; } } if (i == numberSupportedModes) { glusLogPrint(GLUS_LOG_ERROR, "No matching display resolution found: ", width, height); return EGL_NO_SURFACE ; } vc_tv_register_callback(resizeDone, 0); if (vc_tv_hdmi_power_on_explicit_new(group, supportedModes[i].group, supportedModes[i].code) != 0) { vc_tv_unregister_callback(resizeDone); glusLogPrint(GLUS_LOG_ERROR, "Could not switch to full screen: ", width, height); return EGL_NO_SURFACE ; } waitResizeDone(); vc_tv_unregister_callback(resizeDone); windowWidth = width; windowHeight = height; _fullscreen = GLUS_TRUE; } else { windowWidth = width; windowHeight = height; } // _nativeDisplay = vc_dispmanx_display_open(0 /* LCD */); if (!_nativeDisplay) { glusLogPrint(GLUS_LOG_ERROR, "Could not open display"); return EGL_NO_SURFACE ; } // if (SDL_Init(SDL_INIT_VIDEO) != 0) { glusLogPrint(GLUS_LOG_ERROR, "Could not initialize SDL"); return EGL_NO_SURFACE ; } videoInfo = SDL_GetVideoInfo(); if (!videoInfo) { glusLogPrint(GLUS_LOG_ERROR, "Could not get video info for SDL"); return EGL_NO_SURFACE ; } if (!SDL_SetVideoMode(videoInfo->current_w, videoInfo->current_h, videoInfo->vfmt->BitsPerPixel, SDL_HWSURFACE)) { glusLogPrint(GLUS_LOG_ERROR, "Set video mode for SDL failed"); return EGL_NO_SURFACE ; } SDL_ShowCursor(SDL_DISABLE); // dstRect.x = 0; dstRect.y = 0; dstRect.width = windowWidth; dstRect.height = windowHeight; srcRect.x = 0; srcRect.y = 0; srcRect.width = windowWidth << 16; srcRect.height = windowHeight << 16; dispmanAlpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; dispmanAlpha.mask = 0xFFFFFFFF; dispmanAlpha.opacity = 255; dispmanUpdate = vc_dispmanx_update_start(0); dispmanElement = vc_dispmanx_element_add(dispmanUpdate, _nativeDisplay, 0 /*layer*/, &dstRect, 0 /*src*/, &srcRect, DISPMANX_PROTECTION_NONE, &dispmanAlpha, 0/*clamp*/, 0/*transform*/); success = vc_dispmanx_update_submit_sync(dispmanUpdate); if (!dispmanElement || success < 0) { glusLogPrint(GLUS_LOG_ERROR, "Could not add element"); return EGL_NO_SURFACE ; } _width = windowWidth; _height = windowHeight; _nativeWindow.element = dispmanElement; _nativeWindow.width = windowWidth; _nativeWindow.height = windowHeight; _nativeWindowCreated = GLUS_TRUE; return (EGLNativeWindowType)&_nativeWindow; }