I've added a simple plugin to compiz that provides a way to render video
efficiently on a composited desktop.
A video playback client basically copies video data to a pixmap and sets
an X property on a client window that describes the image format of the
data and where it should be rendered.
The compiz video plugin will scale and perform necessary colorspace
conversions when compositing the desktop.
It provides very efficient playback of video on a composited desktop and
it requires much less resources than e.g. XVideo as no intermediate
buffer is required and hence also no accelerated offscreen rendering.
RGB and YV12 image formats are currently supported but it's of course
very easy to add support for additional formats.
YV12 format requires GL_ARB_fragment_program and 8 bpp alpha only GLX
pixmap support. The server doesn't provide alpha only pixmaps today. The
attached patch adds an alternative PICT_a8 visual to composite and that
is enough for xgl to support alpha only GLX pixmaps (not sure it will
work with aiglx or nvidia's driver, I would guess not).
Adding an alpha only X visual might be a bad idea. It doesn't cause any
issues with the clients I've been running but I've seen that GDK thinks
the server is broken and spits out some warnings when it finds a visual
with masks set to 0. Might be a better idea to just add the fbconfig and
no visual but the current GLX code in the server relies on every
fbconfig having a matching X visual so it will need a bit more work and
I'd like some feedback before I start doing anything like that.
I've created a patch to mplayer's xv output code that makes it use
compiz video interface when available. Works pretty well and it even
dynamically detects when the compiz video interface isn't available
anymore and then switches to xv instead. Similar patches can easily be
created for other video playback clients, of course.
- David
diff --git a/composite/compinit.c b/composite/compinit.c
index 5a13612..bd7eeb6 100644
--- a/composite/compinit.c
+++ b/composite/compinit.c
@@ -180,6 +180,7 @@ static CompAlternateVisual altVisuals[NUM_COMP_ALTERNATE_VISUALS] = {
{ 24, PICT_r8g8b8 },
#endif
{ 32, PICT_a8r8g8b8 },
+ { 8, PICT_a8 }
};
static Bool
diff --git a/composite/compint.h b/composite/compint.h
index 9395512..3156135 100644
--- a/composite/compint.h
+++ b/composite/compint.h
@@ -110,9 +110,9 @@ typedef struct _CompSubwindows {
#endif
#if COMP_INCLUDE_RGB24_VISUAL
-#define NUM_COMP_ALTERNATE_VISUALS 2
+#define NUM_COMP_ALTERNATE_VISUALS 3
#else
-#define NUM_COMP_ALTERNATE_VISUALS 1
+#define NUM_COMP_ALTERNATE_VISUALS 2
#endif
typedef struct _CompOverlayClientRec *CompOverlayClientPtr;
--- libvo/vo_xv.c (revision 22417)
+++ libvo/vo_xv.c (working copy)
@@ -92,6 +92,517 @@
static uint32_t dwidth, dheight;
static uint32_t max_width = 0, max_height = 0; // zero means: not set
+#include "libswscale/swscale.h"
+#include <X11/Xatom.h>
+
+#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;
+
+ aspect (&w, &h, A_NOZOOM);
+
+ data[2] = w;
+ data[3] = h;
+ data[4] = vo_panscan * 65536;
+
+ data[5] = GRAVITY_NORTH | GRAVITY_WEST;
+ data[6] = 0;
+ data[7] = 0;
+ data[8] = GRAVITY_SOUTH | GRAVITY_EAST;
+ data[9] = 0;
+ data[10] = 0;
+
+ XChangeProperty (mDisplay,
+ vo_window,
+ vo_compiz_video_atom,
+ XA_INTEGER,
+ 32, PropModeReplace, (unsigned char *) data,
+ 11);
+
+ 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 + 1) & ~1;
+ 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 +963,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 +1060,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 +1109,7 @@
if ( visible_buf != -1 )
{
/* redraw the last visible buffer */
+ if (!vo_pixmap)
put_xvimage( xvimage[visible_buf] );
}
}
@@ -599,13 +1117,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 +1155,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 +1197,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 +1229,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 +1451,8 @@
fo = XvListImageFormats(mDisplay, xv_port, (int *) &formats);
+ vo_compiz_init ();
+
return 0;
}
@@ -940,7 +1480,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;
_______________________________________________
compiz mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/compiz