diff --git a/README.md b/README.md index 66e02ba..9865c49 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,20 @@ $ export VDPAU_DISABLE_G2D=1 If using G2D (A10/A20), make sure to have write access to `/dev/g2d`. +# Play video on second screen + +Because X video driver is baypassed and own disp layers are directly used, physical screen number must be specified using VDPAU_SCREEN env var. If VDPAU_SCREEN is not specified, the same screen number than the one used by the X server will be used. + +This is enough for the most common Xorg configurations: + +* 1 screen: Xorg screen 0 is physical screen 0 +* 2 screens: Xorg screen 0 is physical screen 0 and Xorg screen 1 is physical screen 1. + +If your Xorg configuration is different, you must set VDPAU_SCREEN env var to 0 or 1 (even if using extended desktop configurations, see limitations below). + # Limitations: * Output bypasses X video driver by opening own disp layers. You can't use Xv from fbturbo at the same time, and on H3 the video is always on top and can't be overlapped by other windows. * OSD partly breaks X11 integration due to hardware limitations. The video area can't be overlapped by other windows. For fullscreen use this is no problem. * There is no [OpenGL interoperation feature] (https://www.opengl.org/registry/specs/NV/vdpau_interop.txt) because we are on ARM and only have OpenGL/ES available. +* VDPAU_SCREEN must be specified if Xorg screen numbers doesn't match physical screen numbers. Also if video is played in second monitor when using a extended desktop configuration (such as Xinerama). In this case, video can't be played partially in one monitor and partially in the other. diff --git a/device.c b/device.c index 76ad565..546d5af 100644 --- a/device.c +++ b/device.c @@ -51,6 +51,17 @@ VdpStatus vdp_imp_device_create_x11(Display *display, char *env_vdpau_osd = getenv("VDPAU_OSD"); char *env_vdpau_g2d = getenv("VDPAU_DISABLE_G2D"); + char *env_vdpau_screen = getenv("VDPAU_SCREEN"); + + if (env_vdpau_screen) { + dev->screen_i = atoi(env_vdpau_screen); + VDPAU_DBG("VDPAU_SCREEN set to %d", dev->screen_i); + } + else { + dev->screen_i = screen; // try same than X screen + VDPAU_DBG("VDPAU_SCREEN not set, trying screen %d", dev->screen_i); + } + if (env_vdpau_osd && strncmp(env_vdpau_osd, "1", 1) == 0) dev->osd_enabled = 1; else @@ -71,7 +82,7 @@ VdpStatus vdp_imp_device_create_x11(Display *display, if (!dev->g2d_enabled) VDPAU_DBG("OSD enabled, using pixman"); - + return VDP_STATUS_OK; } diff --git a/presentation_queue.c b/presentation_queue.c index 2ed39ca..b10f2d1 100644 --- a/presentation_queue.c +++ b/presentation_queue.c @@ -55,7 +55,7 @@ VdpStatus vdp_presentation_queue_target_create_x11(VdpDevice device, qt->drawable = drawable; XSetWindowBackground(dev->display, drawable, 0x000102); - qt->disp = sunxi_disp_open(dev->osd_enabled); + qt->disp = sunxi_disp_open(dev->screen_i, dev->osd_enabled); if (!qt->disp) qt->disp = sunxi_disp2_open(dev->osd_enabled); diff --git a/sunxi_disp.c b/sunxi_disp.c index 89a029b..dbebfd8 100644 --- a/sunxi_disp.c +++ b/sunxi_disp.c @@ -33,6 +33,11 @@ struct sunxi_disp_private int fd; int video_layer; int osd_layer; + int sc_src_width; + int sc_src_height; + int sc_width; + int sc_height; + int screen_i; __disp_layer_info_t video_info; __disp_layer_info_t osd_info; }; @@ -43,7 +48,7 @@ static void sunxi_disp_close_video_layer(struct sunxi_disp *sunxi_disp); static int sunxi_disp_set_osd_layer(struct sunxi_disp *sunxi_disp, int x, int y, int width, int height, output_surface_ctx_t *surface); static void sunxi_disp_close_osd_layer(struct sunxi_disp *sunxi_disp); -struct sunxi_disp *sunxi_disp_open(int osd_enabled) +struct sunxi_disp *sunxi_disp_open(int screen_i, int osd_enabled) { struct sunxi_disp_private *disp = calloc(1, sizeof(*disp)); @@ -55,7 +60,23 @@ struct sunxi_disp *sunxi_disp_open(int osd_enabled) if (ioctl(disp->fd, DISP_CMD_VERSION, &tmp) < 0) goto err_version; - uint32_t args[4] = { 0, DISP_LAYER_WORK_MODE_SCALER, 0, 0 }; + disp->screen_i = screen_i; + + __disp_layer_info_t layer_info; + uint32_t args[4] = { disp->screen_i, 100, (uint32_t)&layer_info, 0 }; + disp->sc_src_width = 1; + disp->sc_src_height = 1; + disp->sc_width = 1; + disp->sc_height = 1; + if(ioctl(disp->fd, DISP_CMD_LAYER_GET_PARA, args) >= 0 && layer_info.mode == DISP_LAYER_WORK_MODE_SCALER) + { + disp->sc_src_width = layer_info.src_win.width; + disp->sc_src_height = layer_info.src_win.height; + disp->sc_width = layer_info.scn_win.width; + disp->sc_height = layer_info.scn_win.height; + } + args[1] = DISP_LAYER_WORK_MODE_SCALER; + args[2] = 0; disp->video_layer = ioctl(disp->fd, DISP_CMD_LAYER_REQUEST, args); if (disp->video_layer == 0) goto err_video_layer; @@ -124,7 +145,7 @@ static void sunxi_disp_close(struct sunxi_disp *sunxi_disp) { struct sunxi_disp_private *disp = (struct sunxi_disp_private *)sunxi_disp; - uint32_t args[4] = { 0, disp->video_layer, 0, 0 }; + uint32_t args[4] = { disp->screen_i, disp->video_layer, 0, 0 }; ioctl(disp->fd, DISP_CMD_LAYER_CLOSE, args); ioctl(disp->fd, DISP_CMD_LAYER_RELEASE, args); @@ -182,10 +203,10 @@ static int sunxi_disp_set_video_layer(struct sunxi_disp *sunxi_disp, int x, int disp->video_info.src_win.y = surface->video_src_rect.y0; disp->video_info.src_win.width = surface->video_src_rect.x1 - surface->video_src_rect.x0; disp->video_info.src_win.height = surface->video_src_rect.y1 - surface->video_src_rect.y0; - disp->video_info.scn_win.x = x + surface->video_dst_rect.x0; - disp->video_info.scn_win.y = y + surface->video_dst_rect.y0; - disp->video_info.scn_win.width = surface->video_dst_rect.x1 - surface->video_dst_rect.x0; - disp->video_info.scn_win.height = surface->video_dst_rect.y1 - surface->video_dst_rect.y0; + disp->video_info.scn_win.x = x * disp->sc_width / disp->sc_src_width + surface->video_dst_rect.x0 * disp->sc_width / disp->sc_src_width; + disp->video_info.scn_win.y = y * disp->sc_height / disp->sc_src_height + surface->video_dst_rect.y0 * disp->sc_height / disp->sc_src_height; + disp->video_info.scn_win.width = (surface->video_dst_rect.x1 - surface->video_dst_rect.x0) * disp->sc_width / disp->sc_src_width; + disp->video_info.scn_win.height = (surface->video_dst_rect.y1 - surface->video_dst_rect.y0) * disp->sc_height / disp->sc_src_height; if (disp->video_info.scn_win.y < 0) { @@ -197,7 +218,7 @@ static int sunxi_disp_set_video_layer(struct sunxi_disp *sunxi_disp, int x, int disp->video_info.scn_win.height -= scn_clip; } - uint32_t args[4] = { 0, disp->video_layer, (unsigned long)(&disp->video_info), 0 }; + uint32_t args[4] = { disp->screen_i, disp->video_layer, (unsigned long)(&disp->video_info), 0 }; ioctl(disp->fd, DISP_CMD_LAYER_SET_PARA, args); ioctl(disp->fd, DISP_CMD_LAYER_OPEN, args); @@ -230,7 +251,7 @@ static void sunxi_disp_close_video_layer(struct sunxi_disp *sunxi_disp) { struct sunxi_disp_private *disp = (struct sunxi_disp_private *)sunxi_disp; - uint32_t args[4] = { 0, disp->video_layer, 0, 0 }; + uint32_t args[4] = { disp->screen_i, disp->video_layer, 0, 0 }; ioctl(disp->fd, DISP_CMD_LAYER_CLOSE, args); } @@ -261,7 +282,7 @@ static int sunxi_disp_set_osd_layer(struct sunxi_disp *sunxi_disp, int x, int y, disp->osd_info.scn_win.width = min_nz(width, surface->rgba.dirty.x1) - surface->rgba.dirty.x0; disp->osd_info.scn_win.height = min_nz(height, surface->rgba.dirty.y1) - surface->rgba.dirty.y0; - uint32_t args[4] = { 0, disp->osd_layer, (unsigned long)(&disp->osd_info), 0 }; + uint32_t args[4] = { disp->screen_i, disp->osd_layer, (unsigned long)(&disp->osd_info), 0 }; ioctl(disp->fd, DISP_CMD_LAYER_SET_PARA, args); ioctl(disp->fd, DISP_CMD_LAYER_OPEN, args); @@ -273,6 +294,6 @@ static void sunxi_disp_close_osd_layer(struct sunxi_disp *sunxi_disp) { struct sunxi_disp_private *disp = (struct sunxi_disp_private *)sunxi_disp; - uint32_t args[4] = { 0, disp->osd_layer, 0, 0 }; + uint32_t args[4] = { disp->screen_i, disp->osd_layer, 0, 0 }; ioctl(disp->fd, DISP_CMD_LAYER_CLOSE, args); } diff --git a/sunxi_disp.h b/sunxi_disp.h index b81d6d5..6d71cbb 100644 --- a/sunxi_disp.h +++ b/sunxi_disp.h @@ -31,7 +31,7 @@ struct sunxi_disp void (*close_osd_layer)(struct sunxi_disp *sunxi_disp); }; -struct sunxi_disp *sunxi_disp_open(int osd_enabled); +struct sunxi_disp *sunxi_disp_open(int screen_i, int osd_enabled); struct sunxi_disp *sunxi_disp2_open(int osd_enabled); struct sunxi_disp *sunxi_disp1_5_open(int osd_enabled); diff --git a/vdpau_private.h b/vdpau_private.h index 945c715..4958500 100644 --- a/vdpau_private.h +++ b/vdpau_private.h @@ -38,13 +38,14 @@ typedef struct { cedrus_t *cedrus; Display *display; - int screen; + int screen; // X screen VdpPreemptionCallback *preemption_callback; void *preemption_callback_context; int fd; int g2d_fd; int osd_enabled; int g2d_enabled; + int screen_i; // sunxi disp screen } device_ctx_t; typedef struct