--- libvo/vo_xv.c (revision 22417) +++ libvo/vo_xv.c (working copy) @@ -92,6 +92,520 @@ static uint32_t dwidth, dheight; static uint32_t max_width = 0, max_height = 0; // zero means: not set +#include "libswscale/swscale.h" +#include + +#define GRAVITY_WEST (1 << 0) +#define GRAVITY_EAST (1 << 1) +#define GRAVITY_NORTH (1 << 2) +#define GRAVITY_SOUTH (1 << 3) + +#define COMPIZ_IMAGE_FORMAT_RGB (1 << 0) +#define COMPIZ_IMAGE_FORMAT_YV12 (1 << 1) + +static Atom vo_supporting_wm_check_atom; +static Atom vo_compiz_video_atom; +static Atom vo_compiz_video_supported_atom; +static Atom vo_compiz_video_image_format_rgb_atom; +static Atom vo_compiz_video_image_format_yv12_atom; +static int vo_compiz_formats = 0; +static int vo_compiz_format = 0; +static Pixmap vo_pixmap = None; +static GC vo_pixmap_gc = None; +static Atom vo_xprop = None; +static XImage *ximage = NULL; +static XShmSegmentInfo compizShminfo; +static int pixmap_width; +static int pixmap_height; +static int pixmap_height; +static int pixmap_depth; +static int pixmap_offsets[3]; +static int pixmap_pitches[3]; +static struct SwsContext *swsContext = NULL; +static Window wm_check_window = None; + +static int x11_get_property (Atom type, Atom **args, unsigned long *nitems) +{ + int format; + unsigned long bytesafter; + + return (Success == + XGetWindowProperty(mDisplay, mRootWin, type, 0, 16384, False, + AnyPropertyType, &type, &format, nitems, + &bytesafter, (unsigned char **) args) + && *nitems > 0); +} + +static int error_handler (Display *dpy, XErrorEvent *e) +{ + return 0; +} + +static Bool x11_update_wm_check_window (void) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + + result = XGetWindowProperty (mDisplay, mRootWin, + vo_supporting_wm_check_atom, + 0L, 1L, False, XA_WINDOW, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + XErrorHandler old; + Window win, root; + unsigned int ui; + int i; + + memcpy (&win, data, sizeof (Window)); + XFree ((void *) data); + + old = XSetErrorHandler (error_handler); + + XSelectInput (mDisplay, win, StructureNotifyMask); + + if (!XGetGeometry (mDisplay, win, &root, &i, &i, &ui, &ui, &ui, &ui)) + win = None; + + XSetErrorHandler (old); + + wm_check_window = win; + if (wm_check_window) + return True; + } + + return False; +} + +static void vo_compiz_detect (void) +{ + unsigned long nitems; + Atom *args = NULL; + int formats = 0; + int i; + + if (!x11_update_wm_check_window ()) + { + vo_compiz_formats = 0; + return; + } + + if (x11_get_property (vo_compiz_video_supported_atom, &args, &nitems)) + { + mp_msg (MSGT_VO, MSGL_V, "[xv] Detected wm supports compiz video.\n"); + + for (i = 0; i < nitems; i++) + { + if (args[i] == vo_compiz_video_image_format_rgb_atom) + formats |= COMPIZ_IMAGE_FORMAT_RGB; + else if (args[i] == vo_compiz_video_image_format_yv12_atom) + formats |= COMPIZ_IMAGE_FORMAT_YV12; + } + + XFree (args); + } + + vo_compiz_formats = formats; +} + +static void vo_compiz_init (void) +{ + vo_supporting_wm_check_atom = + XInternAtom (mDisplay, "_NET_SUPPORTING_WM_CHECK", False); + + vo_compiz_video_atom = XInternAtom (mDisplay, + "_COMPIZ_VIDEO", + False); + + vo_compiz_video_supported_atom = XInternAtom (mDisplay, + "_COMPIZ_VIDEO_SUPPORTED", + False); + + vo_compiz_video_image_format_rgb_atom = + XInternAtom (mDisplay, + "_COMPIZ_VIDEO_IMAGE_FORMAT_RGB", + False); + + vo_compiz_video_image_format_yv12_atom = + XInternAtom (mDisplay, + "_COMPIZ_VIDEO_IMAGE_FORMAT_YV12", + False); + + XSelectInput (mDisplay, mRootWin, PropertyChangeMask); + + vo_compiz_detect (); +} + +static void allocate_ximage (void) +{ +#ifdef HAVE_SHM + if (Shmem_Flag) + { + ximage = + XShmCreateImage (mDisplay, NULL, pixmap_depth, ZPixmap, NULL, + &compizShminfo, pixmap_width, pixmap_height); + compizShminfo.shmid = shmget (IPC_PRIVATE, + ximage->bytes_per_line * + ximage->height, IPC_CREAT | 0777); + compizShminfo.shmaddr = (char *) shmat (compizShminfo.shmid, 0, 0); + compizShminfo.readOnly = False; + + ximage->data = compizShminfo.shmaddr; + XShmAttach (mDisplay, &compizShminfo); + XSync (mDisplay, False); + shmctl (compizShminfo.shmid, IPC_RMID, 0); + } else +#endif + { + ximage = + XShmCreateImage (mDisplay, NULL, pixmap_depth, ZPixmap, NULL, + &compizShminfo, pixmap_width, pixmap_height); + ximage->data = malloc (ximage->bytes_per_line * + ximage->height); + XSync (mDisplay, False); + } + + memset (ximage->data, 128, ximage->bytes_per_line * ximage->height); +} + +static void deallocate_ximage (void) +{ +#ifdef HAVE_SHM + if (Shmem_Flag) + { + XShmDetach (mDisplay, &compizShminfo); + shmdt (compizShminfo.shmaddr); + } else +#endif + { + free (ximage->data); + } + + XFree (ximage); + XSync (mDisplay, False); +} + +static void vo_compiz_put_ximage (XImage *myximage) +{ + +#ifdef HAVE_SHM + if (Shmem_Flag) + { + XShmPutImage (mDisplay, vo_pixmap, vo_pixmap_gc, myximage, + 0, 0, + 0, 0, + myximage->width, myximage->height, + True); + } else +#endif + { + XPutImage (mDisplay, vo_pixmap, vo_pixmap_gc, myximage, + 0, 0, + 0, 0, + myximage->width, myximage->height); + } + + if (!vo_xprop) + { + long data[11]; + int w, h; + + data[0] = vo_pixmap; + + if (vo_compiz_format == COMPIZ_IMAGE_FORMAT_YV12) + data[1] = vo_compiz_video_image_format_yv12_atom; + else + data[1] = vo_compiz_video_image_format_rgb_atom; + + data[2] = image_width; + data[3] = image_height; + + aspect (&w, &h, A_NOZOOM); + + data[4] = w; + data[5] = h; + data[6] = vo_panscan * 65536; + + data[7] = GRAVITY_NORTH | GRAVITY_WEST; + data[8] = 0; + data[9] = 0; + data[10] = GRAVITY_SOUTH | GRAVITY_EAST; + data[11] = 0; + data[12] = 0; + + XChangeProperty (mDisplay, + vo_window, + vo_compiz_video_atom, + XA_INTEGER, + 32, PropModeReplace, (unsigned char *) data, + 13); + + vo_xprop = 1; + } + + XSync (mDisplay, False); +} + +static void vo_compiz_check_output (void) +{ + int format = 0; + + if (vo_compiz_formats) + { + if (xv_format == IMGFMT_YV12) + format = vo_compiz_formats & COMPIZ_IMAGE_FORMAT_YV12; + + if (!format) + format = vo_compiz_formats & COMPIZ_IMAGE_FORMAT_RGB; + } + + if (format != vo_compiz_format) + { + if (ximage) + { + deallocate_ximage (); + ximage = NULL; + } + + if (vo_pixmap) + { + XFreePixmap (mDisplay, vo_pixmap); + vo_pixmap = None; + } + + if (swsContext) + { + sws_freeContext (swsContext); + swsContext = NULL; + } + + vo_compiz_format = format; + } + + if (format) + { + int width, height; + + if (format == COMPIZ_IMAGE_FORMAT_YV12) + { + width = (image_width + 7) & ~7; + height = (image_height + 1) & ~1; + + pixmap_width = width; + pixmap_height = height + height / 2; + pixmap_depth = 8; + + pixmap_offsets[0] = 0; + pixmap_offsets[1] = width * height; + pixmap_offsets[2] = width * height + (width / 2); + + pixmap_pitches[0] = width; + pixmap_pitches[1] = width; + pixmap_pitches[2] = width; + + mp_msg (MSGT_VO, MSGL_V, + "[xv] using compiz composited yv12 output\n"); + } + else + { + width = image_width; + height = image_height; + + pixmap_width = width; + pixmap_height = height; + pixmap_depth = 24; + + swsContext = (struct SwsContext *) + sws_getContextFromCmdLine (width, height, image_format, + width, height, IMGFMT_BGR32); + + mp_msg (MSGT_VO, MSGL_V, + "[xv] using compiz composited rgb output\n"); + } + + vo_pixmap = XCreatePixmap (mDisplay, mRootWin, + pixmap_width, pixmap_height, + pixmap_depth); + + if (vo_pixmap_gc != None) + XFreeGC (mDisplay, vo_pixmap_gc); + + allocate_ximage (); + + vo_pixmap_gc = XCreateGC (mDisplay, vo_pixmap, 0L, NULL); + vo_xprop = None; + + XSetWindowBackground (mDisplay, vo_window, 0); + XClearWindow (mDisplay, vo_window); + } + else + { + XDeleteProperty (mDisplay, vo_window, vo_compiz_video_atom); + + mp_msg (MSGT_VO, MSGL_V, + "[xv] compiz composited output not available\n"); + } +} + +static Bool check_compiz_event (Display *display, XEvent *event, XPointer arg) +{ + if (event->type == DestroyNotify) + { + if (event->xdestroywindow.window == wm_check_window) + return True; + } + else if (event->type == PropertyNotify) + { + if (event->xproperty.atom == vo_compiz_video_supported_atom) + return True; + } + + return False; +} + +static void vo_compiz_check_events (void) +{ + XEvent event; + + if (XCheckIfEvent (mDisplay, &event, check_compiz_event, NULL)) + { + vo_compiz_detect (); + vo_compiz_check_output (); + } +} + +static int vo_compiz_draw_slice (uint8_t * image[], int stride[], + int w, int h, int x, int y) +{ + if (vo_compiz_format == COMPIZ_IMAGE_FORMAT_YV12) + { + uint8_t *dst; + + dst = ximage->data + pixmap_offsets[0] + pixmap_pitches[0] * y + x; + memcpy_pic (dst, image[0], w, h, pixmap_pitches[0], stride[0]); + + x /= 2; + y /= 2; + w /= 2; + h /= 2; + + dst = ximage->data + pixmap_offsets[1] + pixmap_pitches[1] * y + x; + memcpy_pic (dst, image[2], w, h, pixmap_pitches[1], stride[2]); + + dst = ximage->data + pixmap_offsets[2] + pixmap_pitches[2] * y + x; + memcpy_pic (dst, image[1], w, h, pixmap_pitches[2], stride[1]); + } + else + { + uint8_t *dst[3]; + int dstStride[3]; + + dstStride[1] = dstStride[2] = 0; + dst[1] = dst[2] = NULL; + + dst[0] = ximage->data; + dstStride[0] = ximage->bytes_per_line; + + sws_scale (swsContext, image, stride, y, h, dst, dstStride); + } + + return 0; +} + +static uint32_t vo_compiz_draw_image (mp_image_t *mpi) +{ + if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK) + return VO_TRUE; + + if (mpi->flags & MP_IMGFLAG_PLANAR) + { + vo_compiz_draw_slice (mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0); + return VO_TRUE; + } + + return VO_FALSE; +} + +static uint32_t vo_compiz_get_image (mp_image_t *mpi) +{ + if (mpi->imgfmt != image_format) + return VO_FALSE; + + if (mpi->height != image_height) + return VO_FALSE; + + if (vo_compiz_format == COMPIZ_IMAGE_FORMAT_YV12) + { + if (mpi->width * (mpi->bpp / 8) != pixmap_pitches[0]) + return VO_FALSE; + + if (!(mpi->flags & MP_IMGFLAG_PLANAR)) + return VO_FALSE; + + if (mpi->flags & MP_IMGFLAG_SWAPPED) + return VO_FALSE; + + if (mpi->flags & (MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_ACCEPT_WIDTH)) + { + mpi->planes[0] = ximage->data + pixmap_offsets[0]; + mpi->stride[0] = pixmap_pitches[0]; + mpi->planes[1] = ximage->data + pixmap_offsets[2]; + mpi->stride[1] = pixmap_pitches[2]; + mpi->planes[2] = ximage->data + pixmap_offsets[1]; + mpi->stride[2] = pixmap_pitches[1]; + + mpi->width = mpi->stride[0] / (mpi->bpp / 8); + } + else + { + return VO_FALSE; + } + } + else + { + if (mpi->type != MP_IMGTYPE_STATIC) + return VO_FALSE; + + if (mpi->flags & (MP_IMGFLAG_PLANAR | MP_IMGFLAG_YUV)) + return VO_FALSE; + + if (mpi->width != image_width) + return VO_FALSE; + + mpi->stride[0] = image_width * 4; + mpi->planes[0] = ximage->data; + } + + mpi->flags |= MP_IMGFLAG_DIRECT; + + return VO_TRUE; +} + +static void vo_compiz_draw_alpha (int x0, int y0, int w, int h, + unsigned char *src, unsigned char *srca, + int stride) +{ + x0 += image_width * (vo_panscan_x >> 1) / (vo_dwidth + vo_panscan_x); + + if (vo_compiz_format == COMPIZ_IMAGE_FORMAT_YV12) + { + vo_draw_alpha_yv12 (w, h, src, srca, stride, + ximage->data + + pixmap_offsets[0] + + pixmap_pitches[0] * y0 + x0, + pixmap_pitches[0]); + } + else + { + vo_draw_alpha_rgb32 (w, h, src, srca, stride, + ximage->data + 4 * (y0 * image_width + x0), + 4 * image_width); + } +} + static void (*draw_alpha_fnc) (int x0, int y0, int w, int h, unsigned char *src, unsigned char *srca, int stride); @@ -452,6 +966,8 @@ } #endif + vo_compiz_check_output (); + mp_msg(MSGT_VO, MSGL_V, "[xv] dx: %d dy: %d dw: %d dh: %d\n", drwX, drwY, vo_dwidth, vo_dheight); @@ -547,8 +1063,12 @@ static void check_events(void) { - int e = vo_x11_check_events(mDisplay); + int e; + vo_compiz_check_events (); + + e = vo_x11_check_events(mDisplay); + if (e & VO_EVENT_RESIZE) { XGetGeometry(mDisplay, vo_window, &mRoot, &drwX, &drwY, &vo_dwidth, @@ -592,6 +1112,7 @@ if ( visible_buf != -1 ) { /* redraw the last visible buffer */ + if (!vo_pixmap) put_xvimage( xvimage[visible_buf] ); } } @@ -599,13 +1120,24 @@ static void draw_osd(void) { - vo_draw_text(image_width - - image_width * vo_panscan_x / (vo_dwidth + vo_panscan_x), - image_height, draw_alpha_fnc); + if (vo_pixmap) + vo_draw_text (image_width - + image_width * vo_panscan_x / (vo_dwidth + vo_panscan_x), + image_height, vo_compiz_draw_alpha); + else + vo_draw_text (image_width - + image_width * vo_panscan_x / (vo_dwidth + vo_panscan_x), + image_height, draw_alpha_fnc); } static void flip_page(void) { + if (vo_pixmap) + { + vo_compiz_put_ximage (ximage); + return; + } + put_xvimage( xvimage[current_buf] ); /* remember the currently visible buffer */ @@ -626,6 +1158,9 @@ { uint8_t *dst; + if (vo_pixmap) + return vo_compiz_draw_slice (image, stride, w, h, x, y); + dst = xvimage[current_buf]->data + xvimage[current_buf]->offsets[0] + xvimage[current_buf]->pitches[0] * y + x; memcpy_pic(dst, image[0], w, h, xvimage[current_buf]->pitches[0], @@ -665,6 +1200,9 @@ static uint32_t draw_image(mp_image_t * mpi) { + if (vo_pixmap) + return vo_compiz_draw_image (mpi); + if (mpi->flags & MP_IMGFLAG_DIRECT) { // direct rendering: @@ -694,6 +1232,9 @@ { int buf = current_buf; // we shouldn't change current_buf unless we do DR! + if (vo_pixmap) + return vo_compiz_get_image (mpi); + if (mpi->type == MP_IMGTYPE_STATIC && num_buffers > 1) return VO_FALSE; // it is not static if (mpi->imgfmt != image_format) @@ -913,6 +1454,8 @@ fo = XvListImageFormats(mDisplay, xv_port, (int *) &formats); + vo_compiz_init (); + return 0; } @@ -940,7 +1483,12 @@ vo_x11_fullscreen(); /* indended, fallthrough to update panscan on fullscreen/windowed switch */ case VOCTRL_SET_PANSCAN: - if ((vo_fs && (vo_panscan != vo_panscan_amount)) + if (vo_pixmap) + { + panscan_calc (); + vo_xprop = None; + } + else if ((vo_fs && (vo_panscan != vo_panscan_amount)) || (!vo_fs && vo_panscan_amount)) { int old_y = vo_panscan_y;