summaryrefslogtreecommitdiff
path: root/src/video_out/video_out_xcbshm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_out/video_out_xcbshm.c')
-rw-r--r--src/video_out/video_out_xcbshm.c1274
1 files changed, 1274 insertions, 0 deletions
diff --git a/src/video_out/video_out_xcbshm.c b/src/video_out/video_out_xcbshm.c
new file mode 100644
index 000000000..3e45c7fdb
--- /dev/null
+++ b/src/video_out/video_out_xcbshm.c
@@ -0,0 +1,1274 @@
+/*
+ * Copyright (C) 2000-2003, 2007 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine 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.
+ *
+ * xine 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
+ *
+ * $Id: video_out_xcbshm.c,v 1.1 2007/02/15 15:19:33 dgp85 Exp $
+ *
+ * video_out_xcbshm.c, X11 shared memory extension interface for xine
+ *
+ * based on mpeg2dec code from
+ * Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * xine-specific code by Guenter Bartsch <bartscgr@studbox.uni-stuttgart.de>
+ *
+ * ported to xcb by Christoph Pfister - Feb 2007
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "xine.h"
+#include "video_out.h"
+
+#include <errno.h>
+
+#include <xcb/shm.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/time.h>
+
+#include <pthread.h>
+#include <netinet/in.h>
+
+#define LOG_MODULE "video_out_xcbshm"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+
+#include "xine_internal.h"
+#include "yuv2rgb.h"
+#include "xineutils.h"
+#include "vo_scale.h"
+#include "xcbosd.h"
+
+typedef struct {
+ vo_frame_t vo_frame;
+
+ /* frame properties as delivered by the decoder: */
+ /* obs: for width/height use vo_scale_t struct */
+ int format;
+ int flags;
+
+ vo_scale_t sc;
+
+ uint8_t *image;
+ int bytes_per_line;
+ xcb_shm_seg_t shmseg;
+
+ uint8_t *chunk[3]; /* mem alloc by xmalloc_aligned */
+
+ yuv2rgb_t *yuv2rgb; /* yuv2rgb converter set up for this frame */
+ uint8_t *rgb_dst;
+
+} xshm_frame_t;
+
+typedef struct {
+
+ vo_driver_t vo_driver;
+
+ /* xcb / shm related stuff */
+ xcb_connection_t *connection;
+ xcb_screen_t *screen;
+ xcb_window_t window;
+ xcb_gcontext_t gc;
+ int depth;
+ int bpp;
+ int scanline_pad;
+ int use_shm;
+
+ int yuv2rgb_brightness;
+ int yuv2rgb_contrast;
+ int yuv2rgb_saturation;
+ uint8_t *yuv2rgb_cmap;
+ yuv2rgb_factory_t *yuv2rgb_factory;
+
+ vo_scale_t sc;
+
+ xshm_frame_t *cur_frame;
+ xcbosd *xoverlay;
+ int ovl_changed;
+
+ xine_t *xine;
+
+ alphablend_t alphablend_extra_data;
+
+ pthread_mutex_t main_mutex;
+
+} xshm_driver_t;
+
+typedef struct {
+ video_driver_class_t driver_class;
+
+ config_values_t *config;
+ xine_t *xine;
+} xshm_class_t;
+
+
+/*
+ * allocate an XImage, try XShm first but fall back to
+ * plain X11 if XShm should fail
+ */
+static void create_ximage(xshm_driver_t *this, xshm_frame_t *frame, int width, int height)
+{
+ frame->bytes_per_line = ((this->bpp * width + this->scanline_pad - 1) &
+ (~(this->scanline_pad - 1))) >> 3;
+
+ if (this->use_shm) {
+ int shmid;
+ xcb_void_cookie_t shm_attach_cookie;
+ xcb_generic_error_t *generic_error;
+
+ /*
+ * try shm
+ */
+
+ shmid = shmget(IPC_PRIVATE, frame->bytes_per_line * height, IPC_CREAT | 0777);
+
+ if (shmid < 0) {
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ _("video_out_xshm: %s: allocating image\n"
+ "video_out_xshm: => not using MIT Shared Memory extension.\n"), strerror(errno));
+ goto shm_fail1;
+ }
+
+ frame->image = shmat(shmid, 0, 0);
+
+ if (frame->image == ((void *) -1)) {
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ _("video_out_xshm: shared memory error (address error) when allocating image \n"
+ "video_out_xshm: => not using MIT Shared Memory extension.\n"));
+ goto shm_fail2;
+ }
+
+ frame->shmseg = xcb_generate_id(this->connection);
+ shm_attach_cookie = xcb_shm_attach_checked(this->connection, frame->shmseg, shmid, 0);
+ generic_error = xcb_request_check(this->connection, shm_attach_cookie);
+
+ if (generic_error != NULL) {
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ _("video_out_xshm: x11 error during shared memory XImage creation\n"
+ "video_out_xshm: => not using MIT Shared Memory extension.\n"));
+ free(generic_error);
+ goto shm_fail3;
+ }
+
+ /*
+ * Now that the Xserver has learned about and attached to the
+ * shared memory segment, delete it. It's actually deleted by
+ * the kernel when all users of that segment have detached from
+ * it. Gives an automatic shared memory cleanup in case we crash.
+ */
+
+ shmctl(shmid, IPC_RMID, 0);
+
+ return;
+
+ shm_fail3:
+ frame->shmseg = 0;
+ shmdt(frame->image);
+ shm_fail2:
+ shmctl(shmid, IPC_RMID, 0);
+ shm_fail1:
+ this->use_shm = 0;
+ }
+
+ /*
+ * fall back to plain X11 if necessary
+ */
+
+ frame->image = malloc(frame->bytes_per_line * height);
+}
+
+static void dispose_ximage(xshm_driver_t *this, xshm_frame_t *frame)
+{
+ if (frame->shmseg) {
+ xcb_shm_detach(this->connection, frame->shmseg);
+ frame->shmseg = 0;
+ shmdt(frame->image);
+ } else
+ free(frame->image);
+ frame->image = NULL;
+}
+
+
+/*
+ * and now, the driver functions
+ */
+
+static uint32_t xshm_get_capabilities (vo_driver_t *this_gen) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+ uint32_t capabilities = VO_CAP_YV12 | VO_CAP_YUY2;
+
+ if( this->xoverlay )
+ capabilities |= VO_CAP_UNSCALED_OVERLAY;
+
+ return capabilities;
+}
+
+static void xshm_frame_proc_slice (vo_frame_t *vo_img, uint8_t **src) {
+ xshm_frame_t *frame = (xshm_frame_t *) vo_img ;
+ /*xshm_driver_t *this = (xshm_driver_t *) vo_img->driver; */
+
+ vo_img->proc_called = 1;
+
+ if( frame->vo_frame.crop_left || frame->vo_frame.crop_top ||
+ frame->vo_frame.crop_right || frame->vo_frame.crop_bottom )
+ {
+ /* we don't support crop, so don't even waste cpu cycles.
+ * cropping will be performed by video_out.c
+ */
+ return;
+ }
+
+ lprintf ("copy... (format %d)\n", frame->format);
+
+ if (frame->format == XINE_IMGFMT_YV12)
+ frame->yuv2rgb->yuv2rgb_fun (frame->yuv2rgb, frame->rgb_dst,
+ src[0], src[1], src[2]);
+ else
+ frame->yuv2rgb->yuy22rgb_fun (frame->yuv2rgb, frame->rgb_dst,
+ src[0]);
+
+ lprintf ("copy...done\n");
+}
+
+static void xshm_frame_field (vo_frame_t *vo_img, int which_field) {
+ xshm_frame_t *frame = (xshm_frame_t *) vo_img ;
+ /* xshm_driver_t *this = (xshm_driver_t *) vo_img->driver; */
+
+ switch (which_field) {
+ case VO_TOP_FIELD:
+ frame->rgb_dst = frame->image;
+ break;
+ case VO_BOTTOM_FIELD:
+ frame->rgb_dst = frame->image + frame->bytes_per_line;
+ break;
+ case VO_BOTH_FIELDS:
+ frame->rgb_dst = frame->image;
+ break;
+ }
+
+ frame->yuv2rgb->next_slice (frame->yuv2rgb, NULL);
+}
+
+static void xshm_frame_dispose (vo_frame_t *vo_img) {
+ xshm_frame_t *frame = (xshm_frame_t *) vo_img ;
+ xshm_driver_t *this = (xshm_driver_t *) vo_img->driver;
+
+ if (frame->image) {
+ pthread_mutex_lock(&this->main_mutex);
+ dispose_ximage(this, frame);
+ pthread_mutex_unlock(&this->main_mutex);
+ }
+
+ frame->yuv2rgb->dispose (frame->yuv2rgb);
+
+ free (frame->chunk[0]);
+ free (frame->chunk[1]);
+ free (frame->chunk[2]);
+ free (frame);
+}
+
+
+static vo_frame_t *xshm_alloc_frame (vo_driver_t *this_gen) {
+ xshm_frame_t *frame;
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+
+ frame = (xshm_frame_t *) xine_xmalloc (sizeof (xshm_frame_t));
+ if (!frame)
+ return NULL;
+
+ memcpy (&frame->sc, &this->sc, sizeof(vo_scale_t));
+
+ pthread_mutex_init (&frame->vo_frame.mutex, NULL);
+
+ /*
+ * supply required functions/fields
+ */
+
+ frame->vo_frame.proc_slice = xshm_frame_proc_slice;
+ frame->vo_frame.proc_frame = NULL;
+ frame->vo_frame.field = xshm_frame_field;
+ frame->vo_frame.dispose = xshm_frame_dispose;
+ frame->vo_frame.driver = this_gen;
+
+ /*
+ * colorspace converter for this frame
+ */
+
+ frame->yuv2rgb = this->yuv2rgb_factory->create_converter (this->yuv2rgb_factory);
+
+ return (vo_frame_t *) frame;
+}
+
+static void xshm_compute_ideal_size (xshm_driver_t *this, xshm_frame_t *frame) {
+ _x_vo_scale_compute_ideal_size( &frame->sc );
+}
+
+static void xshm_compute_rgb_size (xshm_driver_t *this, xshm_frame_t *frame) {
+ _x_vo_scale_compute_output_size( &frame->sc );
+
+ /* avoid problems in yuv2rgb */
+ if (frame->sc.output_height < 1)
+ frame->sc.output_height = 1;
+ if (frame->sc.output_width < 8)
+ frame->sc.output_width = 8;
+ if (frame->sc.output_width & 1) /* yuv2rgb_mlib needs an even YUV2 width */
+ frame->sc.output_width++;
+
+ lprintf("frame source (%d) %d x %d => screen output %d x %d%s\n",
+ frame->vo_frame.id,
+ frame->sc.delivered_width, frame->sc.delivered_height,
+ frame->sc.output_width, frame->sc.output_height,
+ ( frame->sc.delivered_width != frame->sc.output_width
+ || frame->sc.delivered_height != frame->sc.output_height
+ ? ", software scaling"
+ : "" )
+ );
+}
+
+static void xshm_update_frame_format (vo_driver_t *this_gen,
+ vo_frame_t *frame_gen,
+ uint32_t width, uint32_t height,
+ double ratio, int format, int flags) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+ xshm_frame_t *frame = (xshm_frame_t *) frame_gen;
+ int do_adapt;
+ int gui_width;
+ int gui_height;
+ double gui_pixel_aspect;
+
+ flags &= VO_BOTH_FIELDS;
+
+ /* ask gui what output size we'll have for this frame */
+ /* get the gui_pixel_aspect before calling xshm_compute_ideal_size() */
+ /* note: gui_width and gui_height may be bogus because we may have not yet*/
+ /* updated video_pixel_aspect (see _x_vo_scale_compute_ideal_size). */
+ frame->sc.dest_size_cb (frame->sc.user_data, width, height,
+ frame->sc.video_pixel_aspect,
+ &gui_width, &gui_height,
+ &gui_pixel_aspect);
+
+ /* find out if we need to adapt this frame */
+ do_adapt = 0;
+
+ if ((width != frame->sc.delivered_width)
+ || (height != frame->sc.delivered_height)
+ || (ratio != frame->sc.delivered_ratio)
+ || (flags != frame->flags)
+ || (format != frame->format)
+ || (gui_pixel_aspect != frame->sc.gui_pixel_aspect)
+ || (this->sc.user_ratio != frame->sc.user_ratio)) {
+
+ do_adapt = 1;
+
+ lprintf ("frame format (from decoder) has changed => adapt\n");
+
+ frame->sc.delivered_width = width;
+ frame->sc.delivered_height = height;
+ frame->sc.delivered_ratio = ratio;
+ frame->sc.gui_pixel_aspect = gui_pixel_aspect;
+ frame->flags = flags;
+ frame->format = format;
+ frame->sc.user_ratio = this->sc.user_ratio;
+
+ xshm_compute_ideal_size (this, frame);
+
+ /* now we have updated video_aspect_pixel we use the callback */
+ /* again to obtain the correct gui_width and gui_height values. */
+ frame->sc.dest_size_cb (frame->sc.user_data, width, height,
+ frame->sc.video_pixel_aspect,
+ &gui_width, &gui_height,
+ &gui_pixel_aspect);
+ }
+
+ if ((frame->sc.gui_width != gui_width) ||
+ (frame->sc.gui_height != gui_height) ||
+ do_adapt) {
+
+ do_adapt = 1;
+ frame->sc.gui_width = gui_width;
+ frame->sc.gui_height = gui_height;
+
+ xshm_compute_rgb_size (this, frame);
+
+ lprintf ("gui_size has changed => adapt\n");
+ }
+
+
+ /* ok, now do what we have to do */
+
+ if (do_adapt) {
+
+ lprintf ("updating frame to %d x %d\n",
+ frame->sc.output_width, frame->sc.output_height);
+
+ pthread_mutex_lock(&this->main_mutex);
+
+ /*
+ * (re-) allocate XImage
+ */
+
+ if (frame->image) {
+
+ dispose_ximage(this, frame);
+
+ if (frame->chunk[0]){
+ free (frame->chunk[0]);
+ frame->chunk[0] = NULL;
+ }
+ if (frame->chunk[1]) {
+ free (frame->chunk[1]);
+ frame->chunk[1] = NULL;
+ }
+ if (frame->chunk[2]) {
+ free (frame->chunk[2]);
+ frame->chunk[2] = NULL;
+ }
+ }
+
+ create_ximage(this, frame, frame->sc.output_width, frame->sc.output_height);
+
+ pthread_mutex_unlock(&this->main_mutex);
+
+ if (format == XINE_IMGFMT_YV12) {
+ frame->vo_frame.pitches[0] = 8*((width + 7) / 8);
+ frame->vo_frame.pitches[1] = 8*((width + 15) / 16);
+ frame->vo_frame.pitches[2] = 8*((width + 15) / 16);
+ frame->vo_frame.base[0] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[0] * height, (void **) &frame->chunk[0]);
+ frame->vo_frame.base[1] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[1] * ((height+1)/2), (void **) &frame->chunk[1]);
+ frame->vo_frame.base[2] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[2] * ((height+1)/2), (void **) &frame->chunk[2]);
+ } else {
+ frame->vo_frame.pitches[0] = 8*((width + 3) / 4);
+ frame->vo_frame.base[0] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[0] * height, (void **) &frame->chunk[0]);
+ frame->chunk[1] = NULL;
+ frame->chunk[2] = NULL;
+ }
+
+ lprintf ("stripe out_ht=%i, deliv_ht=%i\n",
+ frame->sc.output_height, frame->sc.delivered_height);
+
+ /*
+ * set up colorspace converter
+ */
+
+ switch (flags) {
+ case VO_TOP_FIELD:
+ case VO_BOTTOM_FIELD:
+ frame->yuv2rgb->configure (frame->yuv2rgb,
+ frame->sc.delivered_width,
+ frame->sc.delivered_height,
+ 2*frame->vo_frame.pitches[0],
+ 2*frame->vo_frame.pitches[1],
+ frame->sc.output_width,
+ frame->sc.output_height,
+ frame->bytes_per_line*2);
+ break;
+ case VO_BOTH_FIELDS:
+ frame->yuv2rgb->configure (frame->yuv2rgb,
+ frame->sc.delivered_width,
+ frame->sc.delivered_height,
+ frame->vo_frame.pitches[0],
+ frame->vo_frame.pitches[1],
+ frame->sc.output_width,
+ frame->sc.output_height,
+ frame->bytes_per_line);
+ break;
+ }
+ }
+
+ xshm_frame_field ((vo_frame_t *)frame, flags);
+}
+
+static void xshm_overlay_clut_yuv2rgb(xshm_driver_t *this, vo_overlay_t *overlay,
+ xshm_frame_t *frame) {
+ size_t i;
+ clut_t* clut = (clut_t*) overlay->color;
+
+ if (!overlay->rgb_clut) {
+ for (i = 0; i < sizeof(overlay->color)/sizeof(overlay->color[0]); i++) {
+ *((uint32_t *)&clut[i]) =
+ frame->yuv2rgb->yuv2rgb_single_pixel_fun (frame->yuv2rgb,
+ clut[i].y, clut[i].cb, clut[i].cr);
+ }
+ overlay->rgb_clut++;
+ }
+ if (!overlay->hili_rgb_clut) {
+ clut = (clut_t*) overlay->hili_color;
+ for (i = 0; i < sizeof(overlay->color)/sizeof(overlay->color[0]); i++) {
+ *((uint32_t *)&clut[i]) =
+ frame->yuv2rgb->yuv2rgb_single_pixel_fun(frame->yuv2rgb,
+ clut[i].y, clut[i].cb, clut[i].cr);
+ }
+ overlay->hili_rgb_clut++;
+ }
+}
+
+static void xshm_overlay_begin (vo_driver_t *this_gen,
+ vo_frame_t *frame_gen, int changed) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+
+ this->ovl_changed += changed;
+
+ if( this->ovl_changed && this->xoverlay ) {
+ pthread_mutex_lock(&this->main_mutex);
+ xcbosd_clear(this->xoverlay);
+ pthread_mutex_unlock(&this->main_mutex);
+ }
+
+ this->alphablend_extra_data.offset_x = frame_gen->overlay_offset_x;
+ this->alphablend_extra_data.offset_y = frame_gen->overlay_offset_y;
+}
+
+static void xshm_overlay_end (vo_driver_t *this_gen, vo_frame_t *vo_img) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+
+ if( this->ovl_changed && this->xoverlay ) {
+ pthread_mutex_lock(&this->main_mutex);
+ xcbosd_expose(this->xoverlay);
+ pthread_mutex_unlock(&this->main_mutex);
+ }
+
+ this->ovl_changed = 0;
+}
+
+static void xshm_overlay_blend (vo_driver_t *this_gen,
+ vo_frame_t *frame_gen, vo_overlay_t *overlay) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+ xshm_frame_t *frame = (xshm_frame_t *) frame_gen;
+
+ /* Alpha Blend here */
+ if (overlay->rle) {
+ if( overlay->unscaled ) {
+ if( this->ovl_changed && this->xoverlay ) {
+ pthread_mutex_lock(&this->main_mutex);
+ xcbosd_blend(this->xoverlay, overlay);
+ pthread_mutex_unlock(&this->main_mutex);
+ }
+ } else {
+ if (!overlay->rgb_clut || !overlay->hili_rgb_clut)
+ xshm_overlay_clut_yuv2rgb (this, overlay, frame);
+
+ switch (this->bpp) {
+ case 16:
+ _x_blend_rgb16(frame->image, overlay,
+ frame->sc.output_width, frame->sc.output_height,
+ frame->sc.delivered_width, frame->sc.delivered_height,
+ &this->alphablend_extra_data);
+ break;
+ case 24:
+ _x_blend_rgb24(frame->image, overlay,
+ frame->sc.output_width, frame->sc.output_height,
+ frame->sc.delivered_width, frame->sc.delivered_height,
+ &this->alphablend_extra_data);
+ break;
+ case 32:
+ _x_blend_rgb32(frame->image, overlay,
+ frame->sc.output_width, frame->sc.output_height,
+ frame->sc.delivered_width, frame->sc.delivered_height,
+ &this->alphablend_extra_data);
+ break;
+ default:
+ xprintf(this->xine, XINE_VERBOSITY_DEBUG,
+ "xine-lib:video_out_xshm:xshm_overlay_blend: Cannot blend bpp:%i\n", this->bpp);
+ /* it should never get here, unless a user tries to play in bpp:8 */
+ break;
+ }
+ }
+ }
+}
+
+static void clean_output_area (xshm_driver_t *this, xshm_frame_t *frame) {
+ int i;
+ xcb_rectangle_t rects[4];
+ int rects_count = 0;
+
+ memcpy( this->sc.border, frame->sc.border, sizeof(this->sc.border) );
+
+ pthread_mutex_lock(&this->main_mutex);
+
+ for( i = 0; i < 4; i++ ) {
+ if( this->sc.border[i].w && this->sc.border[i].h )
+ rects[rects_count].x = this->sc.border[i].x;
+ rects[rects_count].y = this->sc.border[i].y;
+ rects[rects_count].width = this->sc.border[i].w;
+ rects[rects_count].height = this->sc.border[i].h;
+ rects_count++;
+ }
+
+ if (rects_count > 0)
+ xcb_poly_fill_rectangle(this->connection, this->window, this->gc, rects_count, rects);
+
+ if (this->xoverlay) {
+ xcbosd_resize(this->xoverlay, this->sc.gui_width, this->sc.gui_height);
+ this->ovl_changed = 1;
+ }
+
+ pthread_mutex_unlock(&this->main_mutex);
+}
+
+static int xshm_redraw_needed (vo_driver_t *this_gen) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+ int ret = 0;
+
+ if( this->cur_frame ) {
+ this->sc.delivered_height = this->cur_frame->sc.delivered_height;
+ this->sc.delivered_width = this->cur_frame->sc.delivered_width;
+ this->sc.video_pixel_aspect = this->cur_frame->sc.video_pixel_aspect;
+ if( _x_vo_scale_redraw_needed( &this->sc ) ) {
+
+ clean_output_area (this, this->cur_frame);
+ ret = 1;
+ }
+ }
+ else
+ ret = 1;
+
+ return ret;
+}
+
+static void xshm_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+ xshm_frame_t *frame = (xshm_frame_t *) frame_gen;
+
+ lprintf ("display frame...\n");
+ lprintf ("about to draw frame (%d) %d x %d...\n",
+ frame->vo_frame.id,
+ frame->sc.output_width, frame->sc.output_height);
+
+ /*
+ * tell gui that we are about to display a frame,
+ * ask for offset
+ */
+
+ this->sc.delivered_height = frame->sc.delivered_height;
+ this->sc.delivered_width = frame->sc.delivered_width;
+ this->sc.video_pixel_aspect = frame->sc.video_pixel_aspect;
+ if( _x_vo_scale_redraw_needed( &this->sc ) ) {
+
+ clean_output_area (this, frame);
+ }
+
+ if (this->cur_frame) {
+
+ if ( (this->cur_frame->sc.output_width != frame->sc.output_width)
+ || (this->cur_frame->sc.output_height != frame->sc.output_height)
+ || (this->cur_frame->sc.output_xoffset != frame->sc.output_xoffset)
+ || (this->cur_frame->sc.output_yoffset != frame->sc.output_yoffset) )
+ clean_output_area (this, frame);
+
+ this->cur_frame->vo_frame.free (&this->cur_frame->vo_frame);
+ }
+
+ this->cur_frame = frame;
+
+ pthread_mutex_lock(&this->main_mutex);
+ lprintf ("display locked...\n");
+
+ if (frame->shmseg) {
+
+ lprintf ("put image (shm)\n");
+ xcb_shm_put_image(this->connection, this->window, this->gc, this->cur_frame->sc.output_width,
+ this->cur_frame->sc.output_height, 0, 0, this->cur_frame->sc.output_width,
+ this->cur_frame->sc.output_height, this->cur_frame->sc.output_xoffset,
+ this->cur_frame->sc.output_yoffset, this->depth, XCB_IMAGE_FORMAT_Z_PIXMAP,
+ 0, this->cur_frame->shmseg, 0);
+
+ } else {
+
+ lprintf ("put image (plain/remote)\n");
+ xcb_put_image(this->connection, XCB_IMAGE_FORMAT_Z_PIXMAP, this->window, this->gc,
+ frame->sc.output_width, frame->sc.output_height, frame->sc.output_xoffset, frame->sc.output_yoffset,
+ 0, this->depth, frame->bytes_per_line * frame->sc.output_height, frame->image);
+
+ }
+ xcb_flush(this->connection);
+ pthread_mutex_unlock(&this->main_mutex);
+
+ lprintf ("display frame done\n");
+}
+
+static int xshm_get_property (vo_driver_t *this_gen, int property) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+
+ switch (property) {
+ case VO_PROP_ASPECT_RATIO:
+ return this->sc.user_ratio;
+ case VO_PROP_MAX_NUM_FRAMES:
+ return 15;
+ case VO_PROP_BRIGHTNESS:
+ return this->yuv2rgb_brightness;
+ case VO_PROP_CONTRAST:
+ return this->yuv2rgb_contrast;
+ case VO_PROP_SATURATION:
+ return this->yuv2rgb_saturation;
+ case VO_PROP_WINDOW_WIDTH:
+ return this->sc.gui_width;
+ case VO_PROP_WINDOW_HEIGHT:
+ return this->sc.gui_height;
+ default:
+ xprintf(this->xine, XINE_VERBOSITY_DEBUG,
+ "video_out_xshm: tried to get unsupported property %d\n", property);
+ }
+
+ return 0;
+}
+
+static int xshm_set_property (vo_driver_t *this_gen,
+ int property, int value) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+
+ if ( property == VO_PROP_ASPECT_RATIO) {
+
+ if (value>=XINE_VO_ASPECT_NUM_RATIOS)
+ value = XINE_VO_ASPECT_AUTO;
+ this->sc.user_ratio = value;
+ xprintf(this->xine, XINE_VERBOSITY_DEBUG,
+ "video_out_xshm: aspect ratio changed to %s\n", _x_vo_scale_aspect_ratio_name(value));
+
+ } else if (property == VO_PROP_BRIGHTNESS) {
+
+ this->yuv2rgb_brightness = value;
+ this->yuv2rgb_factory->set_csc_levels (this->yuv2rgb_factory,
+ this->yuv2rgb_brightness,
+ this->yuv2rgb_contrast,
+ this->yuv2rgb_saturation);
+
+ this->sc.force_redraw = 1;
+
+ } else if (property == VO_PROP_CONTRAST) {
+
+ this->yuv2rgb_contrast = value;
+ this->yuv2rgb_factory->set_csc_levels (this->yuv2rgb_factory,
+ this->yuv2rgb_brightness,
+ this->yuv2rgb_contrast,
+ this->yuv2rgb_saturation);
+
+ this->sc.force_redraw = 1;
+
+ } else if (property == VO_PROP_SATURATION) {
+
+ this->yuv2rgb_saturation = value;
+ this->yuv2rgb_factory->set_csc_levels (this->yuv2rgb_factory,
+ this->yuv2rgb_brightness,
+ this->yuv2rgb_contrast,
+ this->yuv2rgb_saturation);
+
+ this->sc.force_redraw = 1;
+
+ } else {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "video_out_xshm: tried to set unsupported property %d\n", property);
+ }
+
+ return value;
+}
+
+static void xshm_get_property_min_max (vo_driver_t *this_gen,
+ int property, int *min, int *max) {
+ /* xshm_driver_t *this = (xshm_driver_t *) this_gen; */
+
+ if (property == VO_PROP_BRIGHTNESS) {
+ *min = -128;
+ *max = +127;
+ } else if (property == VO_PROP_CONTRAST) {
+ *min = 0;
+ *max = 255;
+ } else if (property == VO_PROP_SATURATION) {
+ *min = 0;
+ *max = 255;
+ } else {
+ *min = 0;
+ *max = 0;
+ }
+}
+
+static int xshm_gui_data_exchange (vo_driver_t *this_gen,
+ int data_type, void *data) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+
+ switch (data_type) {
+#ifndef XINE_DISABLE_DEPRECATED_FEATURES
+ case XINE_GUI_SEND_COMPLETION_EVENT:
+ break;
+#endif
+
+ case XINE_GUI_SEND_EXPOSE_EVENT:
+
+ lprintf ("expose event\n");
+
+ if (this->cur_frame) {
+ xcb_expose_event_t *xev = (xcb_expose_event_t *) data;
+
+ if (xev && xev->count == 0) {
+ int i;
+ xcb_rectangle_t rects[4];
+ int rects_count = 0;
+
+ pthread_mutex_lock(&this->main_mutex);
+ if (this->cur_frame->shmseg)
+ xcb_shm_put_image(this->connection, this->window, this->gc, this->cur_frame->sc.output_width,
+ this->cur_frame->sc.output_height, 0, 0, this->cur_frame->sc.output_width,
+ this->cur_frame->sc.output_height, this->cur_frame->sc.output_xoffset,
+ this->cur_frame->sc.output_yoffset, this->depth, XCB_IMAGE_FORMAT_Z_PIXMAP,
+ 0, this->cur_frame->shmseg, 0);
+ else
+ xcb_put_image(this->connection, XCB_IMAGE_FORMAT_Z_PIXMAP, this->window, this->gc,
+ this->cur_frame->sc.output_width, this->cur_frame->sc.output_height,
+ this->cur_frame->sc.output_xoffset, this->cur_frame->sc.output_yoffset,
+ 0, this->depth, this->cur_frame->bytes_per_line * this->cur_frame->sc.output_height,
+ this->cur_frame->image);
+
+ for( i = 0; i < 4; i++ ) {
+ if( this->sc.border[i].w && this->sc.border[i].h )
+ rects[rects_count].x = this->sc.border[i].x;
+ rects[rects_count].y = this->sc.border[i].y;
+ rects[rects_count].width = this->sc.border[i].w;
+ rects[rects_count].height = this->sc.border[i].h;
+ rects_count++;
+ }
+
+ if (rects_count > 0)
+ xcb_poly_fill_rectangle(this->connection, this->window, this->gc, rects_count, rects);
+
+ if(this->xoverlay)
+ xcbosd_expose(this->xoverlay);
+
+ xcb_flush(this->connection);
+ pthread_mutex_unlock(&this->main_mutex);
+ }
+ }
+ break;
+
+ case XINE_GUI_SEND_DRAWABLE_CHANGED:
+ this->window = (xcb_window_t) data;
+
+ pthread_mutex_lock(&this->main_mutex);
+ xcb_free_gc(this->connection, this->gc);
+ this->gc = xcb_generate_id(this->connection);
+ xcb_create_gc(this->connection, this->gc, this->window, XCB_GC_FOREGROUND, &this->screen->black_pixel);
+ if(this->xoverlay)
+ xcbosd_drawable_changed(this->xoverlay, this->window);
+ this->ovl_changed = 1;
+ pthread_mutex_unlock(&this->main_mutex);
+ break;
+
+ case XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO:
+
+ if (this->cur_frame) {
+ x11_rectangle_t *rect = data;
+ int x1, y1, x2, y2;
+
+ _x_vo_scale_translate_gui2video(&this->cur_frame->sc,
+ rect->x, rect->y,
+ &x1, &y1);
+ _x_vo_scale_translate_gui2video(&this->cur_frame->sc,
+ rect->x + rect->w, rect->y + rect->h,
+ &x2, &y2);
+ rect->x = x1;
+ rect->y = y1;
+ rect->w = x2-x1;
+ rect->h = y2-y1;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static void xshm_dispose (vo_driver_t *this_gen) {
+ xshm_driver_t *this = (xshm_driver_t *) this_gen;
+
+ if (this->cur_frame)
+ this->cur_frame->vo_frame.dispose (&this->cur_frame->vo_frame);
+
+ this->yuv2rgb_factory->dispose (this->yuv2rgb_factory);
+
+ pthread_mutex_lock(&this->main_mutex);
+ xcb_free_gc(this->connection, this->gc);
+ pthread_mutex_unlock(&this->main_mutex);
+
+ if( this->xoverlay ) {
+ pthread_mutex_lock(&this->main_mutex);
+ xcbosd_destroy(this->xoverlay);
+ pthread_mutex_unlock(&this->main_mutex);
+ }
+
+ pthread_mutex_destroy(&this->main_mutex);
+
+ _x_alphablend_free(&this->alphablend_extra_data);
+
+ free (this);
+}
+
+static int ImlibPaletteLUTGet(xshm_driver_t *this) {
+ static const xcb_atom_t CARDINAL = 6;
+
+ xcb_intern_atom_cookie_t atom_cookie;
+ xcb_intern_atom_reply_t *atom_reply;
+
+ xcb_get_property_cookie_t prop_cookie;
+ xcb_get_property_reply_t *prop_reply;
+
+ atom_cookie = xcb_intern_atom(this->connection, 0, sizeof("_IMLIB_COLORMAP"), "_IMLIB_COLORMAP");
+ atom_reply = xcb_intern_atom_reply(this->connection, atom_cookie, NULL);
+
+ if (atom_reply == NULL)
+ return 0;
+
+ prop_cookie = xcb_get_property(this->connection, 0, this->window, atom_reply->atom, CARDINAL, 0, 0x7fffffff);
+ prop_reply = xcb_get_property_reply(this->connection, prop_cookie, NULL);
+
+ free(atom_reply);
+
+ if (prop_reply == NULL)
+ return 0;
+
+ if (prop_reply->format == 8) {
+ unsigned int i;
+ unsigned long j;
+ int num_ret = xcb_get_property_value_length(prop_reply);
+ char *retval = xcb_get_property_value(prop_reply);
+
+ j = 1 + retval[0]*4;
+ this->yuv2rgb_cmap = malloc(sizeof(uint8_t) * 32 * 32 * 32);
+ for (i = 0; i < 32 * 32 * 32 && j < num_ret; i++)
+ this->yuv2rgb_cmap[i] = retval[1+4*retval[j++]+3];
+
+ free(prop_reply);
+ return 1;
+ }
+
+ free(prop_reply);
+ return 0;
+}
+
+
+static char *visual_class_name(xcb_visualtype_t *visual) {
+
+ switch (visual->_class) {
+ case XCB_VISUAL_CLASS_STATIC_GRAY:
+ return "StaticGray";
+ case XCB_VISUAL_CLASS_GRAY_SCALE:
+ return "GrayScale";
+ case XCB_VISUAL_CLASS_STATIC_COLOR:
+ return "StaticColor";
+ case XCB_VISUAL_CLASS_PSEUDO_COLOR:
+ return "PseudoColor";
+ case XCB_VISUAL_CLASS_TRUE_COLOR:
+ return "TrueColor";
+ case XCB_VISUAL_CLASS_DIRECT_COLOR:
+ return "DirectColor";
+ default:
+ return "unknown visual class";
+ }
+}
+
+static vo_driver_t *xshm_open_plugin(video_driver_class_t *class_gen, const void *visual_gen) {
+ xshm_class_t *class = (xshm_class_t *) class_gen;
+ config_values_t *config = class->config;
+ xcb_visual_t *visual = (xcb_visual_t *) visual_gen;
+ xshm_driver_t *this;
+ xcb_visualtype_t *visualtype;
+ int mode;
+ int swapped;
+ int cpu_byte_order;
+ int image_byte_order;
+
+ xcb_get_window_attributes_cookie_t window_attrs_cookie;
+ xcb_get_window_attributes_reply_t *window_attrs_reply;
+
+ xcb_get_geometry_cookie_t geometry_cookie;
+ xcb_get_geometry_reply_t *geometry_reply;
+
+ const xcb_query_extension_reply_t *query_extension_reply;
+
+ this = (xshm_driver_t *) xine_xmalloc (sizeof (xshm_driver_t));
+
+ if (!this)
+ return NULL;
+
+ pthread_mutex_init(&this->main_mutex, NULL);
+
+ _x_alphablend_init(&this->alphablend_extra_data, class->xine);
+
+ this->connection = visual->connection;
+ this->screen = visual->screen;
+ this->window = visual->window;
+
+ _x_vo_scale_init( &this->sc, 0, 0, config );
+ this->sc.frame_output_cb = visual->frame_output_cb;
+ this->sc.dest_size_cb = visual->dest_size_cb;
+ this->sc.user_data = visual->user_data;
+
+ this->sc.user_ratio = XINE_VO_ASPECT_AUTO;
+
+ this->cur_frame = NULL;
+ this->gc = xcb_generate_id(this->connection);
+ xcb_create_gc(this->connection, this->gc, this->window, XCB_GC_FOREGROUND, &this->screen->black_pixel);
+ this->xoverlay = NULL;
+ this->ovl_changed = 0;
+
+ this->xine = class->xine;
+
+ this->vo_driver.get_capabilities = xshm_get_capabilities;
+ this->vo_driver.alloc_frame = xshm_alloc_frame;
+ this->vo_driver.update_frame_format = xshm_update_frame_format;
+ this->vo_driver.overlay_begin = xshm_overlay_begin;
+ this->vo_driver.overlay_blend = xshm_overlay_blend;
+ this->vo_driver.overlay_end = xshm_overlay_end;
+ this->vo_driver.display_frame = xshm_display_frame;
+ this->vo_driver.get_property = xshm_get_property;
+ this->vo_driver.set_property = xshm_set_property;
+ this->vo_driver.get_property_min_max = xshm_get_property_min_max;
+ this->vo_driver.gui_data_exchange = xshm_gui_data_exchange;
+ this->vo_driver.dispose = xshm_dispose;
+ this->vo_driver.redraw_needed = xshm_redraw_needed;
+
+ /*
+ *
+ * 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.
+ */
+
+ window_attrs_cookie = xcb_get_window_attributes(this->connection, this->window);
+ geometry_cookie = xcb_get_geometry(this->connection, this->window);
+ xcb_prefetch_extension_data(this->connection, &xcb_shm_id);
+
+ window_attrs_reply = xcb_get_window_attributes_reply(this->connection, window_attrs_cookie, NULL);
+
+ visualtype = NULL;
+ {
+ xcb_depth_t *depth = xcb_screen_allowed_depths_iterator(this->screen).data;
+ xcb_visualtype_t *vis = xcb_depth_visuals(depth);
+ xcb_visualtype_t *vis_end = vis + xcb_depth_visuals_length(depth);
+
+ for (; vis != vis_end; ++vis)
+ if (window_attrs_reply->visual == vis->visual_id) {
+ visualtype = vis;
+ break;
+ }
+ }
+
+ free(window_attrs_reply);
+
+ geometry_reply = xcb_get_geometry_reply(this->connection, geometry_cookie, NULL);
+
+ this->depth = geometry_reply->depth;
+
+ free(geometry_reply);
+
+ if (this->depth>16)
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ _("\n\nWARNING: current display depth is %d. For better performance\n"
+ "a depth of 16 bpp is recommended!\n\n"), this->depth);
+
+ /*
+ * check for X shared memory support
+ */
+
+ query_extension_reply = xcb_get_extension_data(this->connection, &xcb_shm_id);
+ if (query_extension_reply && query_extension_reply->present) {
+ this->use_shm = 1;
+ }
+ else {
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ _("video_out_xshm: MIT shared memory extension not present on display.\n"));
+ this->use_shm = 0;
+ }
+
+ {
+ const xcb_setup_t *setup = xcb_get_setup(this->connection);
+ xcb_format_t *fmt = xcb_setup_pixmap_formats(setup);
+ xcb_format_t *fmt_end = fmt + xcb_setup_pixmap_formats_length(setup);
+
+ for (; fmt != fmt_end; ++fmt)
+ if(fmt->depth == this->depth) {
+ this->bpp = fmt->bits_per_pixel;
+ this->scanline_pad = fmt->scanline_pad;
+ break;
+ }
+
+ if (fmt == fmt_end) {
+ if (this->depth <= 4)
+ this->bpp = 4;
+ else if (this->depth <= 8)
+ this->bpp = 8;
+ else if (this->depth <= 16)
+ this->bpp = 16;
+ else
+ this->bpp = 32;
+ this->scanline_pad = setup->bitmap_format_scanline_pad;
+ }
+
+ image_byte_order = setup->image_byte_order;
+ }
+
+ /*
+ * Is the same byte order in use on the X11 client and server?
+ */
+ cpu_byte_order = htonl(1) == 1 ? XCB_IMAGE_ORDER_MSB_FIRST : XCB_IMAGE_ORDER_LSB_FIRST;
+ swapped = cpu_byte_order != image_byte_order;
+
+ xprintf(this->xine, XINE_VERBOSITY_DEBUG,
+ "video_out_xshm: video mode depth is %d (%d bpp), %s, %sswapped,\n"
+ "\tred: %08x, green: %08x, blue: %08x\n",
+ this->depth, this->bpp,
+ visual_class_name(visualtype),
+ swapped ? "" : "not ",
+ visualtype->red_mask, visualtype->green_mask, visualtype->blue_mask);
+
+ mode = 0;
+
+ switch (visualtype->_class) {
+ case XCB_VISUAL_CLASS_TRUE_COLOR:
+ switch (this->depth) {
+ case 24:
+ case 32:
+ if (this->bpp == 32) {
+ if (visualtype->red_mask == 0x00ff0000)
+ mode = MODE_32_RGB;
+ else
+ mode = MODE_32_BGR;
+ } else {
+ if (visualtype->red_mask == 0x00ff0000)
+ mode = MODE_24_RGB;
+ else
+ mode = MODE_24_BGR;
+ }
+ break;
+ case 16:
+ if (visualtype->red_mask == 0xf800)
+ mode = MODE_16_RGB;
+ else
+ mode = MODE_16_BGR;
+ break;
+ case 15:
+ if (visualtype->red_mask == 0x7C00)
+ mode = MODE_15_RGB;
+ else
+ mode = MODE_15_BGR;
+ break;
+ case 8:
+ if (visualtype->red_mask == 0xE0)
+ mode = MODE_8_RGB; /* Solaris x86: RGB332 */
+ else
+ mode = MODE_8_BGR; /* XFree86: BGR233 */
+ break;
+ }
+ break;
+
+ case XCB_VISUAL_CLASS_STATIC_GRAY:
+ if (this->depth == 8)
+ mode = MODE_8_GRAY;
+ break;
+
+ case XCB_VISUAL_CLASS_PSEUDO_COLOR:
+ case XCB_VISUAL_CLASS_GRAY_SCALE:
+ if (this->depth <= 8 && ImlibPaletteLUTGet(this))
+ mode = MODE_PALETTE;
+ break;
+ }
+
+ if (!mode) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ _("video_out_xshm: your video mode was not recognized, sorry :-(\n"));
+ return NULL;
+ }
+
+ this->yuv2rgb_brightness = 0;
+ this->yuv2rgb_contrast = 128;
+ this->yuv2rgb_saturation = 128;
+
+ this->yuv2rgb_factory = yuv2rgb_factory_init (mode, swapped,
+ this->yuv2rgb_cmap);
+ this->yuv2rgb_factory->set_csc_levels (this->yuv2rgb_factory,
+ this->yuv2rgb_brightness,
+ this->yuv2rgb_contrast,
+ this->yuv2rgb_saturation);
+
+ this->xoverlay = xcbosd_create(this->xine, this->connection, this->screen,
+ this->window, XCBOSD_SHAPED);
+
+ return &this->vo_driver;
+}
+
+/*
+ * class functions
+ */
+
+static char* xshm_get_identifier (video_driver_class_t *this_gen) {
+ return "XShm";
+}
+
+static char* xshm_get_description (video_driver_class_t *this_gen) {
+ return _("xine video output plugin using the MIT X shared memory extension");
+}
+
+static void xshm_dispose_class (video_driver_class_t *this_gen) {
+ xshm_class_t *this = (xshm_class_t *) this_gen;
+
+ free (this);
+}
+
+static void *xshm_init_class (xine_t *xine, void *visual_gen) {
+ xshm_class_t *this = (xshm_class_t *) xine_xmalloc (sizeof (xshm_class_t));
+
+ this->driver_class.open_plugin = xshm_open_plugin;
+ this->driver_class.get_identifier = xshm_get_identifier;
+ this->driver_class.get_description = xshm_get_description;
+ this->driver_class.dispose = xshm_dispose_class;
+ this->config = xine->config;
+ this->xine = xine;
+
+ return this;
+}
+
+
+static const vo_info_t vo_info_xshm = {
+ 6, /* priority */
+ XINE_VISUAL_TYPE_XCB /* visual type */
+};
+
+
+/*
+ * exported plugin catalog entry
+ */
+
+const plugin_info_t xine_plugin_info[] EXPORTED = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_VIDEO_OUT, 21, "xshm", XINE_VERSION_CODE, &vo_info_xshm, xshm_init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};