Home | History | Annotate | Download | only in x11
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2006 Sam Lantinga
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Lesser General Public
      7     License as published by the Free Software Foundation; either
      8     version 2.1 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Lesser General Public License for more details.
     14 
     15     You should have received a copy of the GNU Lesser General Public
     16     License along with this library; if not, write to the Free Software
     17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 /* This is the XFree86 Xv extension implementation of YUV video overlays */
     25 
     26 #if SDL_VIDEO_DRIVER_X11_XV
     27 
     28 #include <X11/Xlib.h>
     29 #ifndef NO_SHARED_MEMORY
     30 #include <sys/ipc.h>
     31 #include <sys/shm.h>
     32 #include <X11/extensions/XShm.h>
     33 #endif
     34 #include "../Xext/extensions/Xvlib.h"
     35 
     36 #include "SDL_x11yuv_c.h"
     37 #include "../SDL_yuvfuncs.h"
     38 
     39 #define XFREE86_REFRESH_HACK
     40 #ifdef XFREE86_REFRESH_HACK
     41 #include "SDL_x11image_c.h"
     42 #endif
     43 
     44 /* Workaround when pitch != width */
     45 #define PITCH_WORKAROUND
     46 
     47 /* Fix for the NVidia GeForce 2 - use the last available adaptor */
     48 /*#define USE_LAST_ADAPTOR*/  /* Apparently the NVidia drivers are fixed */
     49 
     50 /* The functions used to manipulate software video overlays */
     51 static struct private_yuvhwfuncs x11_yuvfuncs = {
     52 	X11_LockYUVOverlay,
     53 	X11_UnlockYUVOverlay,
     54 	X11_DisplayYUVOverlay,
     55 	X11_FreeYUVOverlay
     56 };
     57 
     58 struct private_yuvhwdata {
     59 	int port;
     60 #ifndef NO_SHARED_MEMORY
     61 	int yuv_use_mitshm;
     62 	XShmSegmentInfo yuvshm;
     63 #endif
     64 	SDL_NAME(XvImage) *image;
     65 };
     66 
     67 
     68 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
     69 
     70 #ifndef NO_SHARED_MEMORY
     71 /* Shared memory error handler routine */
     72 static int shm_error;
     73 static int shm_errhandler(Display *d, XErrorEvent *e)
     74 {
     75         if ( e->error_code == BadAccess ) {
     76         	shm_error = True;
     77         	return(0);
     78         } else
     79 		return(X_handler(d,e));
     80 }
     81 #endif /* !NO_SHARED_MEMORY */
     82 
     83 static int xv_error;
     84 static int xv_errhandler(Display *d, XErrorEvent *e)
     85 {
     86         if ( e->error_code == BadMatch ) {
     87         	xv_error = True;
     88         	return(0);
     89         } else
     90 		return(X_handler(d,e));
     91 }
     92 
     93 SDL_Overlay *X11_CreateYUVOverlay(_THIS, int width, int height, Uint32 format, SDL_Surface *display)
     94 {
     95 	SDL_Overlay *overlay;
     96 	struct private_yuvhwdata *hwdata;
     97 	int xv_port;
     98 	unsigned int i, j, k;
     99 	unsigned int adaptors;
    100 	SDL_NAME(XvAdaptorInfo) *ainfo;
    101 	int bpp;
    102 #ifndef NO_SHARED_MEMORY
    103 	XShmSegmentInfo *yuvshm;
    104 #endif
    105 
    106 	/* Look for the XVideo extension with a valid port for this format */
    107 	xv_port = -1;
    108 	if ( (Success == SDL_NAME(XvQueryExtension)(GFX_Display, &j, &j, &j, &j, &j)) &&
    109 	     (Success == SDL_NAME(XvQueryAdaptors)(GFX_Display,
    110 	                                 RootWindow(GFX_Display, SDL_Screen),
    111 	                                 &adaptors, &ainfo)) ) {
    112 #ifdef USE_LAST_ADAPTOR
    113 		for ( i=0; i < adaptors; ++i )
    114 #else
    115 		for ( i=0; (i < adaptors) && (xv_port == -1); ++i )
    116 #endif /* USE_LAST_ADAPTOR */
    117 		{
    118 			/* Check to see if the visual can be used */
    119 			if ( BUGGY_XFREE86(<=, 4001) ) {
    120 				int visual_ok = 0;
    121 				for ( j=0; j<ainfo[i].num_formats; ++j ) {
    122 					if ( ainfo[i].formats[j].visual_id ==
    123 							SDL_Visual->visualid ) {
    124 						visual_ok = 1;
    125 						break;
    126 					}
    127 				}
    128 				if ( ! visual_ok ) {
    129 					continue;
    130 				}
    131 			}
    132 			if ( (ainfo[i].type & XvInputMask) &&
    133 			     (ainfo[i].type & XvImageMask) ) {
    134 				int num_formats;
    135 				SDL_NAME(XvImageFormatValues) *formats;
    136 				formats = SDL_NAME(XvListImageFormats)(GFX_Display,
    137 				              ainfo[i].base_id, &num_formats);
    138 #ifdef USE_LAST_ADAPTOR
    139 				for ( j=0; j < num_formats; ++j )
    140 #else
    141 				for ( j=0; (j < num_formats) && (xv_port == -1); ++j )
    142 #endif /* USE_LAST_ADAPTOR */
    143 				{
    144 					if ( (Uint32)formats[j].id == format ) {
    145 						for ( k=0; k < ainfo[i].num_ports; ++k ) {
    146 							if ( Success == SDL_NAME(XvGrabPort)(GFX_Display, ainfo[i].base_id+k, CurrentTime) ) {
    147 								xv_port = ainfo[i].base_id+k;
    148 								break;
    149 							}
    150 						}
    151 					}
    152 				}
    153 				if ( formats ) {
    154 					XFree(formats);
    155 				}
    156 			}
    157 		}
    158 		SDL_NAME(XvFreeAdaptorInfo)(ainfo);
    159 	}
    160 
    161 	/* Precalculate the bpp for the pitch workaround below */
    162 	switch (format) {
    163 	    /* Add any other cases we need to support... */
    164 	    case SDL_YUY2_OVERLAY:
    165 	    case SDL_UYVY_OVERLAY:
    166 	    case SDL_YVYU_OVERLAY:
    167 		bpp = 2;
    168 		break;
    169 	    default:
    170 		bpp = 1;
    171 		break;
    172 	}
    173 
    174 #if 0
    175     /*
    176      * !!! FIXME:
    177      * "Here are some diffs for X11 and yuv.  Note that the last part 2nd
    178      *  diff should probably be a new call to XvQueryAdaptorFree with ainfo
    179      *  and the number of adaptors, instead of the loop through like I did."
    180      *
    181      *  ACHTUNG: This is broken! It looks like XvFreeAdaptorInfo does this
    182      *  for you, so we end up with a double-free. I need to look at this
    183      *  more closely...  --ryan.
    184      */
    185  	for ( i=0; i < adaptors; ++i ) {
    186  	  if (ainfo[i].name != NULL) Xfree(ainfo[i].name);
    187  	  if (ainfo[i].formats != NULL) Xfree(ainfo[i].formats);
    188    	}
    189  	Xfree(ainfo);
    190 #endif
    191 
    192 	if ( xv_port == -1 ) {
    193 		SDL_SetError("No available video ports for requested format");
    194 		return(NULL);
    195 	}
    196 
    197 	/* Enable auto-painting of the overlay colorkey */
    198 	{
    199 		static const char *attr[] = { "XV_AUTOPAINT_COLORKEY", "XV_AUTOPAINT_COLOURKEY" };
    200 		unsigned int i;
    201 
    202 		SDL_NAME(XvSelectPortNotify)(GFX_Display, xv_port, True);
    203 		X_handler = XSetErrorHandler(xv_errhandler);
    204 		for ( i=0; i < sizeof(attr)/(sizeof attr[0]); ++i ) {
    205 			Atom a;
    206 
    207 			xv_error = False;
    208 			a = XInternAtom(GFX_Display, attr[i], True);
    209 			if ( a != None ) {
    210      				SDL_NAME(XvSetPortAttribute)(GFX_Display, xv_port, a, 1);
    211 				XSync(GFX_Display, True);
    212 				if ( ! xv_error ) {
    213 					break;
    214 				}
    215 			}
    216 		}
    217 		XSetErrorHandler(X_handler);
    218 		SDL_NAME(XvSelectPortNotify)(GFX_Display, xv_port, False);
    219 	}
    220 
    221 	/* Create the overlay structure */
    222 	overlay = (SDL_Overlay *)SDL_malloc(sizeof *overlay);
    223 	if ( overlay == NULL ) {
    224 		SDL_NAME(XvUngrabPort)(GFX_Display, xv_port, CurrentTime);
    225 		SDL_OutOfMemory();
    226 		return(NULL);
    227 	}
    228 	SDL_memset(overlay, 0, (sizeof *overlay));
    229 
    230 	/* Fill in the basic members */
    231 	overlay->format = format;
    232 	overlay->w = width;
    233 	overlay->h = height;
    234 
    235 	/* Set up the YUV surface function structure */
    236 	overlay->hwfuncs = &x11_yuvfuncs;
    237 	overlay->hw_overlay = 1;
    238 
    239 	/* Create the pixel data and lookup tables */
    240 	hwdata = (struct private_yuvhwdata *)SDL_malloc(sizeof *hwdata);
    241 	overlay->hwdata = hwdata;
    242 	if ( hwdata == NULL ) {
    243 		SDL_NAME(XvUngrabPort)(GFX_Display, xv_port, CurrentTime);
    244 		SDL_OutOfMemory();
    245 		SDL_FreeYUVOverlay(overlay);
    246 		return(NULL);
    247 	}
    248 	hwdata->port = xv_port;
    249 #ifndef NO_SHARED_MEMORY
    250 	yuvshm = &hwdata->yuvshm;
    251 	SDL_memset(yuvshm, 0, sizeof(*yuvshm));
    252 	hwdata->image = SDL_NAME(XvShmCreateImage)(GFX_Display, xv_port, format,
    253 						   0, width, height, yuvshm);
    254 #ifdef PITCH_WORKAROUND
    255 	if ( hwdata->image != NULL && hwdata->image->pitches[0] != (width*bpp) ) {
    256 		/* Ajust overlay width according to pitch */
    257 		XFree(hwdata->image);
    258 		width = hwdata->image->pitches[0] / bpp;
    259 		hwdata->image = SDL_NAME(XvShmCreateImage)(GFX_Display, xv_port, format,
    260 							   0, width, height, yuvshm);
    261 	}
    262 #endif /* PITCH_WORKAROUND */
    263 	hwdata->yuv_use_mitshm = (hwdata->image != NULL);
    264 	if ( hwdata->yuv_use_mitshm ) {
    265 		yuvshm->shmid = shmget(IPC_PRIVATE, hwdata->image->data_size,
    266 				       IPC_CREAT | 0777);
    267 		if ( yuvshm->shmid >= 0 ) {
    268 			yuvshm->shmaddr = (char *)shmat(yuvshm->shmid, 0, 0);
    269 			yuvshm->readOnly = False;
    270 			if ( yuvshm->shmaddr != (char *)-1 ) {
    271 				shm_error = False;
    272 				X_handler = XSetErrorHandler(shm_errhandler);
    273 				XShmAttach(GFX_Display, yuvshm);
    274 				XSync(GFX_Display, True);
    275 				XSetErrorHandler(X_handler);
    276 				if ( shm_error )
    277 					shmdt(yuvshm->shmaddr);
    278 			} else {
    279 				shm_error = True;
    280 			}
    281 			shmctl(yuvshm->shmid, IPC_RMID, NULL);
    282 		} else {
    283 			shm_error = True;
    284 		}
    285 		if ( shm_error ) {
    286 			XFree(hwdata->image);
    287 			hwdata->yuv_use_mitshm = 0;
    288 		} else {
    289 			hwdata->image->data = yuvshm->shmaddr;
    290 		}
    291 	}
    292 	if ( !hwdata->yuv_use_mitshm )
    293 #endif /* NO_SHARED_MEMORY */
    294 	{
    295 		hwdata->image = SDL_NAME(XvCreateImage)(GFX_Display, xv_port, format,
    296 							0, width, height);
    297 
    298 #ifdef PITCH_WORKAROUND
    299 		if ( hwdata->image != NULL && hwdata->image->pitches[0] != (width*bpp) ) {
    300 			/* Ajust overlay width according to pitch */
    301 			XFree(hwdata->image);
    302 			width = hwdata->image->pitches[0] / bpp;
    303 			hwdata->image = SDL_NAME(XvCreateImage)(GFX_Display, xv_port, format,
    304 								0, width, height);
    305 		}
    306 #endif /* PITCH_WORKAROUND */
    307 		if ( hwdata->image == NULL ) {
    308 			SDL_SetError("Couldn't create XVideo image");
    309 			SDL_FreeYUVOverlay(overlay);
    310 			return(NULL);
    311 		}
    312 		hwdata->image->data = SDL_malloc(hwdata->image->data_size);
    313 		if ( hwdata->image->data == NULL ) {
    314 			SDL_OutOfMemory();
    315 			SDL_FreeYUVOverlay(overlay);
    316 			return(NULL);
    317 		}
    318 	}
    319 
    320 	/* Find the pitch and offset values for the overlay */
    321 	overlay->planes = hwdata->image->num_planes;
    322 	overlay->pitches = (Uint16 *)SDL_malloc(overlay->planes * sizeof(Uint16));
    323 	overlay->pixels = (Uint8 **)SDL_malloc(overlay->planes * sizeof(Uint8 *));
    324 	if ( !overlay->pitches || !overlay->pixels ) {
    325 		SDL_OutOfMemory();
    326 		SDL_FreeYUVOverlay(overlay);
    327 		return(NULL);
    328 	}
    329 	for ( i=0; i<overlay->planes; ++i ) {
    330 		overlay->pitches[i] = hwdata->image->pitches[i];
    331 		overlay->pixels[i] = (Uint8 *)hwdata->image->data +
    332 		                              hwdata->image->offsets[i];
    333 	}
    334 
    335 #ifdef XFREE86_REFRESH_HACK
    336 	/* Work around an XFree86 X server bug (?)
    337 	   We can't perform normal updates in windows that have video
    338 	   being output to them.  See SDL_x11image.c for more details.
    339 	 */
    340 	X11_DisableAutoRefresh(this);
    341 #endif
    342 
    343 	/* We're all done.. */
    344 	return(overlay);
    345 }
    346 
    347 int X11_LockYUVOverlay(_THIS, SDL_Overlay *overlay)
    348 {
    349 	return(0);
    350 }
    351 
    352 void X11_UnlockYUVOverlay(_THIS, SDL_Overlay *overlay)
    353 {
    354 	return;
    355 }
    356 
    357 int X11_DisplayYUVOverlay(_THIS, SDL_Overlay *overlay, SDL_Rect *src, SDL_Rect *dst)
    358 {
    359 	struct private_yuvhwdata *hwdata;
    360 
    361 	hwdata = overlay->hwdata;
    362 
    363 #ifndef NO_SHARED_MEMORY
    364 	if ( hwdata->yuv_use_mitshm ) {
    365 		SDL_NAME(XvShmPutImage)(GFX_Display, hwdata->port, SDL_Window, SDL_GC,
    366 	              hwdata->image,
    367 		      src->x, src->y, src->w, src->h,
    368 		      dst->x, dst->y, dst->w, dst->h, False);
    369 	}
    370 	else
    371 #endif
    372 	{
    373 		SDL_NAME(XvPutImage)(GFX_Display, hwdata->port, SDL_Window, SDL_GC,
    374 				     hwdata->image,
    375 		                     src->x, src->y, src->w, src->h,
    376 		                     dst->x, dst->y, dst->w, dst->h);
    377 	}
    378 	XSync(GFX_Display, False);
    379 	return(0);
    380 }
    381 
    382 void X11_FreeYUVOverlay(_THIS, SDL_Overlay *overlay)
    383 {
    384 	struct private_yuvhwdata *hwdata;
    385 
    386 	hwdata = overlay->hwdata;
    387 	if ( hwdata ) {
    388 		SDL_NAME(XvUngrabPort)(GFX_Display, hwdata->port, CurrentTime);
    389 #ifndef NO_SHARED_MEMORY
    390 		if ( hwdata->yuv_use_mitshm ) {
    391 			XShmDetach(GFX_Display, &hwdata->yuvshm);
    392 			shmdt(hwdata->yuvshm.shmaddr);
    393 		}
    394 #endif
    395 		if ( hwdata->image ) {
    396 			XFree(hwdata->image);
    397 		}
    398 		SDL_free(hwdata);
    399 	}
    400 	if ( overlay->pitches ) {
    401 		SDL_free(overlay->pitches);
    402 		overlay->pitches = NULL;
    403 	}
    404 	if ( overlay->pixels ) {
    405 		SDL_free(overlay->pixels);
    406 		overlay->pixels = NULL;
    407 	}
    408 #ifdef XFREE86_REFRESH_HACK
    409 	X11_EnableAutoRefresh(this);
    410 #endif
    411 }
    412 
    413 #endif /* SDL_VIDEO_DRIVER_X11_XV */
    414