summaryrefslogtreecommitdiff
path: root/libvo/video_out_x11.c
diff options
context:
space:
mode:
Diffstat (limited to 'libvo/video_out_x11.c')
-rw-r--r--libvo/video_out_x11.c638
1 files changed, 638 insertions, 0 deletions
diff --git a/libvo/video_out_x11.c b/libvo/video_out_x11.c
new file mode 100644
index 0000000..afc7fc2
--- /dev/null
+++ b/libvo/video_out_x11.c
@@ -0,0 +1,638 @@
+/*
+ * video_out_x11.c
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 2003 Regis Duchesne <hpreg@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * mpeg2dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpeg2dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#ifdef LIBVO_X11
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#include <inttypes.h>
+#include <unistd.h>
+/* since it doesn't seem to be defined on some platforms */
+int XShmGetEventBase (Display *);
+
+#ifdef LIBVO_XV
+#include <string.h> /* strcmp */
+#include <X11/extensions/Xvlib.h>
+#define FOURCC_YV12 0x32315659
+#define FOURCC_UYVY 0x59565955
+#endif
+
+#include "mpeg2.h"
+#include "video_out.h"
+#include "vo_internal.h"
+#include "mpeg2convert.h"
+
+typedef struct {
+ void * data;
+ int wait_completion;
+ XImage * ximage;
+#ifdef LIBVO_XV
+ XvImage * xvimage;
+#endif
+} x11_frame_t;
+
+typedef struct x11_instance_s {
+ vo_instance_t vo;
+ x11_frame_t frame[3];
+ int index;
+ int width;
+ int height;
+ Display * display;
+ Window window;
+ GC gc;
+ XVisualInfo vinfo;
+ XShmSegmentInfo shminfo;
+ int xshm_extension;
+ int completion_type;
+ int xshm;
+#ifdef LIBVO_XV
+ unsigned int adaptors;
+ XvAdaptorInfo * adaptorInfo;
+ XvPortID port;
+ int xv;
+#endif
+ void (* teardown) (struct x11_instance_s * instance);
+} x11_instance_t;
+
+static int open_display (x11_instance_t * instance, int width, int height)
+{
+ int major;
+ int minor;
+ Bool pixmaps;
+ XVisualInfo visualTemplate;
+ XVisualInfo * XvisualInfoTable;
+ XVisualInfo * XvisualInfo;
+ int number;
+ int i;
+ XSetWindowAttributes attr;
+ XGCValues gcValues;
+
+ instance->display = XOpenDisplay (NULL);
+ if (! (instance->display)) {
+ fprintf (stderr, "Can not open display\n");
+ return 1;
+ }
+
+ instance->xshm_extension = 0;
+ if (XShmQueryVersion (instance->display, &major, &minor,
+ &pixmaps) != 0 &&
+ (major > 1 || (major == 1 && minor >= 1))) {
+ instance->xshm_extension = 1;
+ instance->completion_type =
+ XShmGetEventBase (instance->display) + ShmCompletion;
+ } else
+ fprintf (stderr, "No xshm extension\n");
+
+ /* list truecolor visuals for the default screen */
+#ifdef __cplusplus
+ visualTemplate.c_class = TrueColor;
+#else
+ visualTemplate.class = TrueColor;
+#endif
+ visualTemplate.screen = DefaultScreen (instance->display);
+ XvisualInfoTable = XGetVisualInfo (instance->display,
+ VisualScreenMask | VisualClassMask,
+ &visualTemplate, &number);
+ if (XvisualInfoTable == NULL) {
+ fprintf (stderr, "No truecolor visual\n");
+ return 1;
+ }
+
+ /* find the visual with the highest depth */
+ XvisualInfo = XvisualInfoTable;
+ for (i = 1; i < number; i++)
+ if (XvisualInfoTable[i].depth > XvisualInfo->depth)
+ XvisualInfo = XvisualInfoTable + i;
+
+ instance->vinfo = *XvisualInfo;
+ XFree (XvisualInfoTable);
+
+ attr.background_pixmap = None;
+ attr.backing_store = NotUseful;
+ attr.border_pixel = 0;
+ attr.event_mask = 0;
+ /* fucking sun blows me - you have to create a colormap there... */
+ attr.colormap = XCreateColormap (instance->display,
+ RootWindow (instance->display,
+ instance->vinfo.screen),
+ instance->vinfo.visual, AllocNone);
+ instance->window =
+ XCreateWindow (instance->display,
+ DefaultRootWindow (instance->display),
+ 0 /* x */, 0 /* y */, width, height,
+ 0 /* border_width */, instance->vinfo.depth,
+ InputOutput, instance->vinfo.visual,
+ (CWBackPixmap | CWBackingStore | CWBorderPixel |
+ CWEventMask | CWColormap), &attr);
+
+ instance->gc = XCreateGC (instance->display, instance->window, 0,
+ &gcValues);
+
+#ifdef LIBVO_XV
+ instance->adaptors = 0;
+ instance->adaptorInfo = NULL;
+#endif
+
+ return 0;
+}
+
+static int shmerror = 0;
+
+static int handle_error (Display * display, XErrorEvent * error)
+{
+ shmerror = 1;
+ return 0;
+}
+
+static void * create_shm (x11_instance_t * instance, int size)
+{
+ instance->shminfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
+ if (instance->shminfo.shmid == -1)
+ goto error;
+
+ instance->shminfo.shmaddr = (char *) shmat (instance->shminfo.shmid, 0, 0);
+ if (instance->shminfo.shmaddr == (char *)-1)
+ goto error;
+
+ /* on linux the IPC_RMID only kicks off once everyone detaches the shm */
+ /* doing this early avoids shm leaks when we are interrupted. */
+ /* this would break the solaris port though :-/ */
+ /* shmctl (instance->shminfo.shmid, IPC_RMID, 0); */
+
+ /* XShmAttach fails on remote displays, so we have to catch this event */
+
+ XSync (instance->display, False);
+ XSetErrorHandler (handle_error);
+
+ instance->shminfo.readOnly = True;
+ if (! (XShmAttach (instance->display, &(instance->shminfo))))
+ shmerror = 1;
+
+ XSync (instance->display, False);
+ XSetErrorHandler (NULL);
+ if (shmerror) {
+ error:
+ fprintf (stderr, "cannot create shared memory\n");
+ if (instance->shminfo.shmid != -1) {
+ shmdt (instance->shminfo.shmaddr);
+ shmctl (instance->shminfo.shmid, IPC_RMID, 0);
+ }
+ return NULL;
+ }
+
+ return instance->shminfo.shmaddr;
+}
+
+static void destroy_shm (x11_instance_t * instance)
+{
+ XShmDetach (instance->display, &(instance->shminfo));
+ shmdt (instance->shminfo.shmaddr);
+ shmctl (instance->shminfo.shmid, IPC_RMID, 0);
+}
+
+static void x11_event (x11_instance_t * instance)
+{
+ XEvent event;
+ char * addr;
+ int i;
+
+ XNextEvent (instance->display, &event);
+ if (event.type == instance->completion_type) {
+ addr = (instance->shminfo.shmaddr +
+ ((XShmCompletionEvent *)&event)->offset);
+ for (i = 0; i < 3; i++)
+ if (addr == instance->frame[i].data)
+ instance->frame[i].wait_completion = 0;
+ }
+}
+
+static void x11_start_fbuf (vo_instance_t * _instance,
+ uint8_t * const * buf, void * id)
+{
+ x11_instance_t * instance = (x11_instance_t *) _instance;
+ x11_frame_t * frame = (x11_frame_t *) id;
+
+ while (frame->wait_completion)
+ x11_event (instance);
+}
+
+static void x11_setup_fbuf (vo_instance_t * _instance,
+ uint8_t ** buf, void ** id)
+{
+ x11_instance_t * instance = (x11_instance_t *) _instance;
+
+ buf[0] = (uint8_t *) instance->frame[instance->index].data;
+ buf[1] = buf[2] = NULL;
+ *id = instance->frame + instance->index++;
+}
+
+static void x11_draw_frame (vo_instance_t * _instance,
+ uint8_t * const * buf, void * id)
+{
+ x11_frame_t * frame;
+ x11_instance_t * instance;
+
+ frame = (x11_frame_t *) id;
+ instance = (x11_instance_t *) _instance;
+ if (instance->xshm)
+ XShmPutImage (instance->display, instance->window, instance->gc,
+ frame->ximage, 0, 0, 0, 0,
+ instance->width, instance->height, True);
+ else
+ XPutImage (instance->display, instance->window, instance->gc,
+ frame->ximage, 0, 0, 0, 0,
+ instance->width, instance->height);
+ XFlush (instance->display);
+ frame->wait_completion = instance->xshm;
+}
+
+static int x11_alloc_frames (x11_instance_t * instance, int xshm)
+{
+ int size;
+ char * alloc;
+ int i = 0;
+
+ if (xshm && !instance->xshm_extension)
+ return 1;
+
+ size = 0;
+ alloc = NULL;
+ while (i < 3) {
+ instance->frame[i].wait_completion = 0;
+ instance->frame[i].ximage = xshm ?
+ XShmCreateImage (instance->display, instance->vinfo.visual,
+ instance->vinfo.depth, ZPixmap, NULL /* data */,
+ &(instance->shminfo),
+ instance->width, instance->height) :
+ XCreateImage(instance->display, instance->vinfo.visual,
+ instance->vinfo.depth, ZPixmap, 0, NULL /* data */,
+ instance->width, instance->height, 8, 0);
+ if (instance->frame[i].ximage == NULL) {
+ fprintf (stderr, "Cannot create ximage\n");
+ return 1;
+ } else if (xshm) {
+ if (i == 0) {
+ size = (instance->frame[0].ximage->bytes_per_line *
+ instance->frame[0].ximage->height);
+ alloc = (char *) create_shm (instance, 3 * size);
+ } else if (size != (instance->frame[i].ximage->bytes_per_line *
+ instance->frame[i].ximage->height)) {
+ fprintf (stderr, "unexpected ximage data size\n");
+ return 1;
+ }
+ } else
+ alloc =
+ (char *) malloc (instance->frame[i].ximage->bytes_per_line *
+ instance->frame[i].ximage->height);
+ instance->frame[i].data = instance->frame[i].ximage->data = alloc;
+ i++;
+ if (alloc == NULL) {
+ while (--i >= 0)
+ XDestroyImage (instance->frame[i].ximage);
+ return 1;
+ }
+ alloc += size;
+ }
+
+ instance->xshm = xshm;
+ return 0;
+}
+
+static void x11_teardown (x11_instance_t * instance)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ while (instance->frame[i].wait_completion)
+ x11_event (instance);
+ XDestroyImage (instance->frame[i].ximage);
+ }
+ if (instance->xshm)
+ destroy_shm (instance);
+}
+
+static void x11_close (vo_instance_t * _instance)
+{
+ x11_instance_t * instance = (x11_instance_t *) _instance;
+
+ if (instance->teardown != NULL)
+ instance->teardown (instance);
+ XFreeGC (instance->display, instance->gc);
+ XDestroyWindow (instance->display, instance->window);
+#ifdef LIBVO_XV
+ if (instance->adaptors)
+ XvFreeAdaptorInfo (instance->adaptorInfo);
+#endif
+ XCloseDisplay (instance->display);
+ free (instance);
+}
+
+#ifdef LIBVO_XV
+static void xv_setup_fbuf (vo_instance_t * _instance,
+ uint8_t ** buf, void ** id)
+{
+ x11_instance_t * instance = (x11_instance_t *) _instance;
+ uint8_t * data;
+
+ data = (uint8_t *) instance->frame[instance->index].xvimage->data;
+ buf[0] = data + instance->frame[instance->index].xvimage->offsets[0];
+ buf[1] = data + instance->frame[instance->index].xvimage->offsets[2];
+ buf[2] = data + instance->frame[instance->index].xvimage->offsets[1];
+ *id = instance->frame + instance->index++;
+}
+
+static void xv_draw_frame (vo_instance_t * _instance,
+ uint8_t * const * buf, void * id)
+{
+ x11_frame_t * frame = (x11_frame_t *) id;
+ x11_instance_t * instance = (x11_instance_t *) _instance;
+
+ if (instance->xshm)
+ XvShmPutImage (instance->display, instance->port, instance->window,
+ instance->gc, frame->xvimage, 0, 0,
+ instance->width, instance->height, 0, 0,
+ instance->width, instance->height, True);
+ else
+ XvPutImage (instance->display, instance->port, instance->window,
+ instance->gc, frame->xvimage, 0, 0,
+ instance->width, instance->height, 0, 0,
+ instance->width, instance->height);
+ XFlush (instance->display);
+ frame->wait_completion = instance->xshm;
+}
+
+static int xv_check_fourcc (x11_instance_t * instance, XvPortID port,
+ int fourcc, const char * fourcc_str)
+{
+ XvImageFormatValues * formatValues;
+ int formats;
+ int i;
+
+ formatValues = XvListImageFormats (instance->display, port, &formats);
+ for (i = 0; i < formats; i++)
+ if ((formatValues[i].id == fourcc) &&
+ (! (strcmp (formatValues[i].guid, fourcc_str)))) {
+ XFree (formatValues);
+ return 0;
+ }
+ XFree (formatValues);
+ return 1;
+}
+
+static int xv_check_extension (x11_instance_t * instance,
+ int fourcc, const char * fourcc_str)
+{
+ unsigned int i;
+ unsigned long j;
+
+ if (!instance->adaptorInfo) {
+ unsigned int version;
+ unsigned int release;
+ unsigned int dummy;
+
+ if ((XvQueryExtension (instance->display, &version, &release,
+ &dummy, &dummy, &dummy) != Success) ||
+ (version < 2) || ((version == 2) && (release < 2))) {
+ fprintf (stderr, "No xv extension\n");
+ return 1;
+ }
+
+ XvQueryAdaptors (instance->display, instance->window,
+ &instance->adaptors, &instance->adaptorInfo);
+ }
+
+ for (i = 0; i < instance->adaptors; i++)
+ if (instance->adaptorInfo[i].type & XvImageMask)
+ for (j = 0; j < instance->adaptorInfo[i].num_ports; j++)
+ if ((! (xv_check_fourcc (instance,
+ instance->adaptorInfo[i].base_id + j,
+ fourcc, fourcc_str))) &&
+ (XvGrabPort (instance->display,
+ instance->adaptorInfo[i].base_id + j,
+ 0) == Success)) {
+ instance->port = instance->adaptorInfo[i].base_id + j;
+ return 0;
+ }
+
+ fprintf (stderr, "Cannot find xv %s port\n", fourcc_str);
+ return 1;
+}
+
+static int xv_alloc_frames (x11_instance_t * instance, int size,
+ int fourcc)
+{
+ char * alloc;
+ int i = 0;
+
+ instance->xshm = 1;
+ alloc = instance->xshm_extension ?
+ (char *) create_shm (instance, 3 * size) : NULL;
+ if (alloc == NULL) {
+ instance->xshm = 0;
+ alloc = (char *) malloc (3 * size);
+ if (alloc == NULL)
+ return 1;
+ }
+
+ while (i < 3) {
+ instance->frame[i].wait_completion = 0;
+ instance->frame[i].xvimage = instance->xshm ?
+ XvShmCreateImage (instance->display, instance->port, fourcc,
+ alloc, instance->width, instance->height,
+ &(instance->shminfo)) :
+ XvCreateImage (instance->display, instance->port, fourcc,
+ alloc, instance->width, instance->height);
+ instance->frame[i].data = alloc;
+ alloc += size;
+ if ((instance->frame[i].xvimage == NULL) ||
+ (instance->frame[i++].xvimage->data_size != size)) {
+ while (--i >= 0)
+ XFree (instance->frame[i].xvimage);
+ if (instance->xshm)
+ destroy_shm (instance);
+ else
+ free (instance->frame[0].data);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void xv_teardown (x11_instance_t * instance)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ while (instance->frame[i].wait_completion)
+ x11_event (instance);
+ XFree (instance->frame[i].xvimage);
+ }
+ if (instance->xshm)
+ destroy_shm (instance);
+ else
+ free (instance->frame[0].data);
+ XvUngrabPort (instance->display, instance->port, 0);
+}
+#endif
+
+static int common_setup (vo_instance_t * _instance, unsigned int width,
+ unsigned int height, unsigned int chroma_width,
+ unsigned int chroma_height,
+ vo_setup_result_t * result)
+{
+ x11_instance_t * instance = (x11_instance_t *) _instance;
+
+ if (instance->display != NULL) {
+ /* Already setup, just adjust to the new size */
+ if (instance->teardown != NULL)
+ instance->teardown (instance);
+ XResizeWindow (instance->display, instance->window, width, height);
+ } else {
+ /* Not setup yet, do the full monty */
+ if (open_display (instance, width, height))
+ return 1;
+ XMapWindow (instance->display, instance->window);
+ }
+ instance->vo.setup_fbuf = NULL;
+ instance->vo.start_fbuf = NULL;
+ instance->vo.set_fbuf = NULL;
+ instance->vo.draw = NULL;
+ instance->vo.discard = NULL;
+ instance->vo.close = x11_close;
+ instance->width = width;
+ instance->height = height;
+ instance->index = 0;
+ instance->teardown = NULL;
+ result->convert = NULL;
+
+#ifdef LIBVO_XV
+ if (instance->xv == 1 &&
+ (chroma_width == width >> 1) && (chroma_height == height >> 1) &&
+ !xv_check_extension (instance, FOURCC_YV12, "YV12") &&
+ !xv_alloc_frames (instance, 3 * width * height / 2, FOURCC_YV12)) {
+ instance->vo.setup_fbuf = xv_setup_fbuf;
+ instance->vo.start_fbuf = x11_start_fbuf;
+ instance->vo.draw = xv_draw_frame;
+ instance->teardown = xv_teardown;
+ } else if (instance->xv && (chroma_width == width >> 1) &&
+ !xv_check_extension (instance, FOURCC_UYVY, "UYVY") &&
+ !xv_alloc_frames (instance, 2 * width * height, FOURCC_UYVY)) {
+ instance->vo.setup_fbuf = x11_setup_fbuf;
+ instance->vo.start_fbuf = x11_start_fbuf;
+ instance->vo.draw = xv_draw_frame;
+ instance->teardown = xv_teardown;
+ result->convert = mpeg2convert_uyvy;
+ } else
+#endif
+ if (!x11_alloc_frames (instance, 1) || !x11_alloc_frames (instance, 0)) {
+ int bpp;
+
+ instance->vo.setup_fbuf = x11_setup_fbuf;
+ instance->vo.start_fbuf = x11_start_fbuf;
+ instance->vo.draw = x11_draw_frame;
+ instance->teardown = x11_teardown;
+
+#ifdef WORDS_BIGENDIAN
+ if (instance->frame[0].ximage->byte_order != MSBFirst) {
+ fprintf (stderr, "No support for non-native byte order\n");
+ return 1;
+ }
+#else
+ if (instance->frame[0].ximage->byte_order != LSBFirst) {
+ fprintf (stderr, "No support for non-native byte order\n");
+ return 1;
+ }
+#endif
+
+ /*
+ * depth in X11 terminology land is the number of bits used to
+ * actually represent the colour.
+ *
+ * bpp in X11 land means how many bits in the frame buffer per
+ * pixel.
+ *
+ * ex. 15 bit color is 15 bit depth and 16 bpp. Also 24 bit
+ * color is 24 bit depth, but can be 24 bpp or 32 bpp.
+ *
+ * If we have blue in the lowest bit then "obviously" RGB
+ * (the guy who wrote this convention never heard of endianness ?)
+ */
+
+ bpp = ((instance->vinfo.depth == 24) ?
+ instance->frame[0].ximage->bits_per_pixel :
+ instance->vinfo.depth);
+ result->convert =
+ mpeg2convert_rgb (((instance->frame[0].ximage->blue_mask & 1) ?
+ MPEG2CONVERT_RGB : MPEG2CONVERT_BGR), bpp);
+ if (result->convert == NULL) {
+ fprintf (stderr, "%dbpp not supported\n", bpp);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static vo_instance_t * common_open (int xv)
+{
+ x11_instance_t * instance;
+
+ instance = (x11_instance_t *) malloc (sizeof (x11_instance_t));
+ if (instance == NULL)
+ return NULL;
+
+ instance->vo.setup = common_setup;
+ instance->vo.close = (void (*) (vo_instance_t *)) free;
+ instance->display = NULL;
+#ifdef LIBVO_XV
+ instance->xv = xv;
+#endif
+ return (vo_instance_t *) instance;
+}
+
+vo_instance_t * vo_x11_open (void)
+{
+ return common_open (0);
+}
+
+#ifdef LIBVO_XV
+vo_instance_t * vo_xv_open (void)
+{
+ return common_open (1);
+}
+
+vo_instance_t * vo_xv2_open (void)
+{
+ return common_open (2);
+}
+#endif
+#endif