static int draw_slice(uint8_t * src[], int stride[], int w, int h, int x, int y) { uint8_t *dst[MP_MAX_PLANES] = {NULL}; int dstStride[MP_MAX_PLANES] = {0}; if ((old_vo_dwidth != vo_dwidth || old_vo_dheight != vo_dheight) /*&& y==0 */ && zoomFlag) { int newW = vo_dwidth; int newH = vo_dheight; struct SwsContext *oldContext = swsContext; old_vo_dwidth = vo_dwidth; old_vo_dheight = vo_dheight; if (vo_fs) aspect(&newW, &newH, A_ZOOM); if (sws_flags == 0) newW &= (~31); // not needed but, if the user wants the FAST_BILINEAR SCALER, then its needed swsContext = sws_getContextFromCmdLine(srcW, srcH, in_format, newW, newH, out_format); if (swsContext) { image_width = (newW + 7) & (~7); image_height = newH; freeMyXImage(); getMyXImage(); sws_freeContext(oldContext); } else { swsContext = oldContext; } dst_width = newW; } dstStride[0] = image_width * ((bpp + 7) / 8); dst[0] = ImageData; if (Flip_Flag) { dst[0] += dstStride[0] * (image_height - 1); dstStride[0] = -dstStride[0]; } sws_scale(swsContext, src, stride, y, h, dst, dstStride); return 0; }
static int config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, char *title, uint32_t format) { // int screen; // int interval, prefer_blank, allow_exp, nothing; Colormap theCmap; const struct fmt2Xfmtentry_s *fmte = fmt2Xfmt; #ifdef CONFIG_XF86VM int vm = flags & VOFLAG_MODESWITCHING; #endif Flip_Flag = flags & VOFLAG_FLIPPING; zoomFlag = flags & VOFLAG_SWSCALE; old_vo_dwidth = -1; old_vo_dheight = -1; int_pause = 0; if (!title) title = "MPlayer X11 (XImage/Shm) render"; in_format = format; srcW = width; srcH = height; XGetWindowAttributes(mDisplay, mRootWin, &attribs); depth = attribs.depth; if (depth != 15 && depth != 16 && depth != 24 && depth != 32) { Visual *visual; depth = vo_find_depth_from_visuals(mDisplay, mScreen, &visual); } if (!XMatchVisualInfo(mDisplay, mScreen, depth, DirectColor, &vinfo) || (WinID > 0 && vinfo.visualid != XVisualIDFromVisual(attribs.visual))) XMatchVisualInfo(mDisplay, mScreen, depth, TrueColor, &vinfo); /* set image size (which is indeed neither the input nor output size), if zoom is on it will be changed during draw_slice anyway so we don't duplicate the aspect code here */ image_width = (width + 7) & (~7); image_height = height; { #ifdef CONFIG_XF86VM if (vm) { vo_vm_switch(); } #endif theCmap = vo_x11_create_colormap(&vinfo); vo_x11_create_vo_window(&vinfo, vo_dx, vo_dy, vo_dwidth, vo_dheight, flags, theCmap, "x11", title); if (WinID > 0) depth = vo_x11_update_geometry(); #ifdef CONFIG_XF86VM if (vm) { /* Grab the mouse pointer in our window */ if (vo_grabpointer) XGrabPointer(mDisplay, vo_window, True, 0, GrabModeAsync, GrabModeAsync, vo_window, None, CurrentTime); XSetInputFocus(mDisplay, vo_window, RevertToNone, CurrentTime); } #endif } if (myximage) { freeMyXImage(); sws_freeContext(swsContext); } getMyXImage(); while (fmte->mpfmt) { int depth = IMGFMT_RGB_DEPTH(fmte->mpfmt); /* bits_per_pixel in X seems to be set to 16 for 15 bit formats => force depth to 16 so that only the color masks are used for the format check */ if (depth == 15) depth = 16; if (depth == myximage->bits_per_pixel && fmte->byte_order == myximage->byte_order && fmte->red_mask == myximage->red_mask && fmte->green_mask == myximage->green_mask && fmte->blue_mask == myximage->blue_mask) break; fmte++; } if (!fmte->mpfmt) { mp_msg(MSGT_VO, MSGL_ERR, "X server image format not supported, please contact the developers\n"); return -1; } out_format = fmte->mpfmt; switch ((bpp = myximage->bits_per_pixel)) { case 24: draw_alpha_fnc = draw_alpha_24; break; case 32: draw_alpha_fnc = draw_alpha_32; break; case 15: case 16: if (depth == 15) draw_alpha_fnc = draw_alpha_15; else draw_alpha_fnc = draw_alpha_16; break; default: draw_alpha_fnc = draw_alpha_null; } out_offset = 0; // for these formats conversion is currently not support and // we can easily "emulate" them. if (out_format & 64 && (IMGFMT_IS_RGB(out_format) || IMGFMT_IS_BGR(out_format))) { out_format &= ~64; #if HAVE_BIGENDIAN out_offset = 1; #else out_offset = -1; #endif } /* always allocate swsContext as size could change between frames */ swsContext = sws_getContextFromCmdLine(width, height, in_format, width, height, out_format); if (!swsContext) return -1; dst_width = width; //printf( "X11 bpp: %d color mask: R:%lX G:%lX B:%lX\n",bpp,myximage->red_mask,myximage->green_mask,myximage->blue_mask ); return 0; }
static int config(struct vo *vo, uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, uint32_t format) { struct priv *p = vo->priv; Colormap theCmap; const struct fmt2Xfmtentry_s *fmte = fmt2Xfmt; #ifdef CONFIG_XF86VM int vm = flags & VOFLAG_MODESWITCHING; #endif p->Flip_Flag = flags & VOFLAG_FLIPPING; p->zoomFlag = 1; p->old_vo_dwidth = -1; p->old_vo_dheight = -1; p->in_format = format; p->srcW = width; p->srcH = height; XGetWindowAttributes(vo->x11->display, vo->x11->rootwin, &p->attribs); p->depth = p->attribs.depth; if (p->depth != 15 && p->depth != 16 && p->depth != 24 && p->depth != 32) { Visual *visual; p->depth = vo_find_depth_from_visuals(vo->x11->display, vo->x11->screen, &visual); } if (!XMatchVisualInfo(vo->x11->display, vo->x11->screen, p->depth, DirectColor, &p->vinfo) || (WinID > 0 && p->vinfo.visualid != XVisualIDFromVisual(p->attribs.visual))) { XMatchVisualInfo(vo->x11->display, vo->x11->screen, p->depth, TrueColor, &p->vinfo); } /* set image size (which is indeed neither the input nor output size), if zoom is on it will be changed during draw_slice anyway so we don't duplicate the aspect code here */ p->image_width = (width + 7) & (~7); p->image_height = height; { #ifdef CONFIG_XF86VM if (vm) vo_vm_switch(vo); #endif theCmap = vo_x11_create_colormap(vo, &p->vinfo); vo_x11_create_vo_window(vo, &p->vinfo, vo->dx, vo->dy, vo->dwidth, vo->dheight, flags, theCmap, "x11"); if (WinID > 0) p->depth = vo_x11_update_geometry(vo, true); #ifdef CONFIG_XF86VM if (vm) { /* Grab the mouse pointer in our window */ if (vo_grabpointer) XGrabPointer(vo->x11->display, vo->x11->window, True, 0, GrabModeAsync, GrabModeAsync, vo->x11->window, None, CurrentTime); XSetInputFocus(vo->x11->display, vo->x11->window, RevertToNone, CurrentTime); } #endif } if (p->myximage) { freeMyXImage(p); sws_freeContext(p->swsContext); } getMyXImage(p); while (fmte->mpfmt) { int depth = IMGFMT_RGB_DEPTH(fmte->mpfmt); /* bits_per_pixel in X seems to be set to 16 for 15 bit formats => force depth to 16 so that only the color masks are used for the format check */ if (depth == 15) depth = 16; if (depth == p->myximage->bits_per_pixel && fmte->byte_order == p->myximage->byte_order && fmte->red_mask == p->myximage->red_mask && fmte->green_mask == p->myximage->green_mask && fmte->blue_mask == p->myximage->blue_mask) break; fmte++; } if (!fmte->mpfmt) { mp_msg( MSGT_VO, MSGL_ERR, "X server image format not supported, please contact the developers\n"); return -1; } p->out_format = fmte->mpfmt; p->bpp = p->myximage->bits_per_pixel; p->out_offset = 0; // We can easily "emulate" non-native RGB32 and BGR32 if (p->out_format == (IMGFMT_BGR32 | 128) || p->out_format == (IMGFMT_RGB32 | 128)) { p->out_format &= ~128; #if BYTE_ORDER == BIG_ENDIAN p->out_offset = 1; #else p->out_offset = -1; #endif } /* always allocate swsContext as size could change between frames */ p->swsContext = sws_getContextFromCmdLine(width, height, p->in_format, width, height, p->out_format); if (!p->swsContext) return -1; p->dst_width = width; return 0; }
static void draw_osd(struct vo *vo, struct osd_state *osd) { struct priv *p = vo->priv; struct mp_image img = get_x_buffer(p); struct mp_osd_res res = { .w = img.w, .h = img.h, .display_par = vo->monitor_par, .video_par = vo->aspdat.par, }; osd_draw_on_image_bk(osd, res, osd->vo_pts, 0, p->osd_backup, &img); } static mp_image_t *get_screenshot(struct vo *vo) { struct priv *p = vo->priv; struct mp_image img = get_x_buffer(p); struct mp_image *res = alloc_mpi(img.w, img.h, img.imgfmt); copy_mpi(res, &img); mp_draw_sub_backup_restore(p->osd_backup, res); return res; } static int redraw_frame(struct vo *vo) { struct priv *p = vo->priv; struct mp_image img = get_x_buffer(p); mp_draw_sub_backup_restore(p->osd_backup, &img); return true; } static void flip_page(struct vo *vo) { struct priv *p = vo->priv; Display_Image(p, p->myximage, p->ImageData); XSync(vo->x11->display, False); } static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, int x, int y) { struct priv *p = vo->priv; uint8_t *dst[MP_MAX_PLANES] = {NULL}; int dstStride[MP_MAX_PLANES] = {0}; if ((p->old_vo_dwidth != vo->dwidth || p->old_vo_dheight != vo->dheight) /*&& y==0 */ && p->zoomFlag) { int newW = vo->dwidth; int newH = vo->dheight; struct SwsContext *oldContext = p->swsContext; p->old_vo_dwidth = vo->dwidth; p->old_vo_dheight = vo->dheight; if (vo_fs) aspect(vo, &newW, &newH, A_ZOOM); if (sws_flags == 0) newW &= (~31); // not needed but, if the user wants the FAST_BILINEAR SCALER, then its needed p->swsContext = sws_getContextFromCmdLine(p->srcW, p->srcH, p->in_format, newW, newH, p->out_format); if (p->swsContext) { p->image_width = (newW + 7) & (~7); p->image_height = newH; freeMyXImage(p); getMyXImage(p); sws_freeContext(oldContext); } else p->swsContext = oldContext; p->dst_width = newW; } dstStride[0] = p->image_width * ((p->bpp + 7) / 8); dst[0] = p->ImageData; if (p->Flip_Flag) { dst[0] += dstStride[0] * (p->image_height - 1); dstStride[0] = -dstStride[0]; } sws_scale(p->swsContext, (const uint8_t **)src, stride, y, h, dst, dstStride); mp_draw_sub_backup_reset(p->osd_backup); return 0; } static int query_format(struct vo *vo, uint32_t format) { mp_msg(MSGT_VO, MSGL_DBG2, "vo_x11: query_format was called: %x (%s)\n", format, vo_format_name(format)); if (IMGFMT_IS_BGR(format)) { if (IMGFMT_BGR_DEPTH(format) <= 8) return 0; // TODO 8bpp not yet fully implemented if (IMGFMT_BGR_DEPTH(format) == vo->x11->depthonscreen) return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_OSD | VFCAP_FLIP | VFCAP_ACCEPT_STRIDE; else return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_FLIP | VFCAP_ACCEPT_STRIDE; } switch (format) { case IMGFMT_I420: case IMGFMT_IYUV: case IMGFMT_YV12: return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_ACCEPT_STRIDE; } return 0; }
static bool resize(struct vo *vo) { struct priv *p = vo->priv; struct vo_x11_state *x11 = vo->x11; for (int i = 0; i < 2; i++) freeMyXImage(p, i); vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd); p->src_w = p->src.x1 - p->src.x0; p->src_h = p->src.y1 - p->src.y0; p->dst_w = p->dst.x1 - p->dst.x0; p->dst_h = p->dst.y1 - p->dst.y0; // p->osd contains the parameters assuming OSD rendering in window // coordinates, but OSD can only be rendered in the intersection // between window and video rectangle (i.e. not into panscan borders). p->osd.w = p->dst_w; p->osd.h = p->dst_h; p->osd.mt = MPMIN(0, p->osd.mt); p->osd.mb = MPMIN(0, p->osd.mb); p->osd.mr = MPMIN(0, p->osd.mr); p->osd.ml = MPMIN(0, p->osd.ml); mp_input_set_mouse_transform(vo->input_ctx, &p->dst, NULL); p->image_width = (p->dst_w + 7) & (~7); p->image_height = p->dst_h; for (int i = 0; i < 2; i++) { if (!getMyXImage(p, i)) return -1; } const struct fmt_entry *fmte = mp_to_x_fmt; while (fmte->mpfmt) { if (fmte->depth == p->myximage[0]->bits_per_pixel && fmte->byte_order == p->myximage[0]->byte_order && fmte->red_mask == p->myximage[0]->red_mask && fmte->green_mask == p->myximage[0]->green_mask && fmte->blue_mask == p->myximage[0]->blue_mask) break; fmte++; } if (!fmte->mpfmt) { MP_ERR(vo, "X server image format not supported, use another VO.\n"); return -1; } mp_sws_set_from_cmdline(p->sws, vo->opts->sws_opts); p->sws->dst = (struct mp_image_params) { .imgfmt = fmte->mpfmt, .w = p->dst_w, .h = p->dst_h, .p_w = 1, .p_h = 1, }; mp_image_params_guess_csp(&p->sws->dst); if (mp_sws_reinit(p->sws) < 0) return false; XFillRectangle(x11->display, x11->window, p->gc, 0, 0, vo->dwidth, vo->dheight); vo->want_redraw = true; return true; } static void Display_Image(struct priv *p, XImage *myximage) { struct vo *vo = p->vo; XImage *x_image = p->myximage[p->current_buf]; #if HAVE_SHM && HAVE_XEXT if (p->Shmem_Flag) { XShmPutImage(vo->x11->display, vo->x11->window, p->gc, x_image, 0, 0, p->dst.x0, p->dst.y0, p->dst_w, p->dst_h, True); vo->x11->ShmCompletionWaitCount++; } else #endif { XPutImage(vo->x11->display, vo->x11->window, p->gc, x_image, 0, 0, p->dst.x0, p->dst.y0, p->dst_w, p->dst_h); } } static struct mp_image get_x_buffer(struct priv *p, int buf_index) { struct mp_image img = {0}; mp_image_set_params(&img, &p->sws->dst); img.planes[0] = p->myximage[buf_index]->data; img.stride[0] = p->image_width * ((p->myximage[buf_index]->bits_per_pixel + 7) / 8); return img; } static void wait_for_completion(struct vo *vo, int max_outstanding) { #if HAVE_SHM && HAVE_XEXT struct priv *ctx = vo->priv; struct vo_x11_state *x11 = vo->x11; if (ctx->Shmem_Flag) { while (x11->ShmCompletionWaitCount > max_outstanding) { if (!ctx->Shm_Warned_Slow) { MP_WARN(vo, "can't keep up! Waiting" " for XShm completion events...\n"); ctx->Shm_Warned_Slow = 1; } mp_sleep_us(1000); vo_x11_check_events(vo); } } #endif } static void flip_page(struct vo *vo) { struct priv *p = vo->priv; Display_Image(p, p->myximage[p->current_buf]); p->current_buf = (p->current_buf + 1) % 2; } // Note: REDRAW_FRAME can call this with NULL. static void draw_image(struct vo *vo, mp_image_t *mpi) { struct priv *p = vo->priv; wait_for_completion(vo, 1); struct mp_image img = get_x_buffer(p, p->current_buf); if (mpi) { struct mp_image src = *mpi; struct mp_rect src_rc = p->src; src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, src.fmt.align_x); src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, src.fmt.align_y); mp_image_crop_rc(&src, src_rc); mp_sws_scale(p->sws, &img, &src); } else { mp_image_clear(&img, 0, 0, img.w, img.h); } osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, &img); if (mpi != p->original_image) { talloc_free(p->original_image); p->original_image = mpi; } } static int query_format(struct vo *vo, int format) { if (sws_isSupportedInput(imgfmt2pixfmt(format))) return 1; return 0; } static void uninit(struct vo *vo) { struct priv *p = vo->priv; if (p->myximage[0]) freeMyXImage(p, 0); if (p->myximage[1]) freeMyXImage(p, 1); if (p->gc) XFreeGC(vo->x11->display, p->gc); talloc_free(p->original_image); vo_x11_uninit(vo); } static int preinit(struct vo *vo) { struct priv *p = vo->priv; p->vo = vo; p->sws = mp_sws_alloc(vo); if (!vo_x11_init(vo)) goto error; struct vo_x11_state *x11 = vo->x11; XWindowAttributes attribs; XGetWindowAttributes(x11->display, x11->rootwin, &attribs); p->depth = attribs.depth; if (!XMatchVisualInfo(x11->display, x11->screen, p->depth, TrueColor, &p->vinfo)) goto error; MP_VERBOSE(vo, "selected visual: %d\n", (int)p->vinfo.visualid); if (!vo_x11_create_vo_window(vo, &p->vinfo, "x11")) goto error; p->gc = XCreateGC(x11->display, x11->window, 0, NULL); MP_WARN(vo, "Warning: this legacy VO has bad performance. Consider fixing " "your graphics drivers, or not forcing the x11 VO.\n"); return 0; error: uninit(vo); return -1; } static int control(struct vo *vo, uint32_t request, void *data) { struct priv *p = vo->priv; switch (request) { case VOCTRL_SET_PANSCAN: if (vo->config_ok) resize(vo); return VO_TRUE; case VOCTRL_REDRAW_FRAME: draw_image(vo, p->original_image); return true; } int events = 0; int r = vo_x11_control(vo, &events, request, data); if (vo->config_ok && (events & (VO_EVENT_EXPOSE | VO_EVENT_RESIZE))) resize(vo); vo_event(vo, events); return r; } const struct vo_driver video_out_x11 = { .description = "X11 (slow, old crap)", .name = "x11", .priv_size = sizeof(struct priv), .preinit = preinit, .query_format = query_format, .reconfig = reconfig, .control = control, .draw_image = draw_image, .flip_page = flip_page, .wakeup = vo_x11_wakeup, .wait_events = vo_x11_wait_events, .uninit = uninit, };