Home | History | Annotate | Download | only in x11
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 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 /* Workaround intel i810 video overlay waiting with failing until the
     48    first Xv[Shm]PutImage call <sigh> */
     49 #define INTEL_XV_BADALLOC_WORKAROUND
     50 
     51 /* Fix for the NVidia GeForce 2 - use the last available adaptor */
     52 /*#define USE_LAST_ADAPTOR*/  /* Apparently the NVidia drivers are fixed */
     53 
     54 /* The functions used to manipulate software video overlays */
     55 static struct private_yuvhwfuncs x11_yuvfuncs = {
     56 	X11_LockYUVOverlay,
     57 	X11_UnlockYUVOverlay,
     58 	X11_DisplayYUVOverlay,
     59 	X11_FreeYUVOverlay
     60 };
     61 
     62 struct private_yuvhwdata {
     63 	int port;
     64 #ifndef NO_SHARED_MEMORY
     65 	int yuv_use_mitshm;
     66 	XShmSegmentInfo yuvshm;
     67 #endif
     68 	SDL_NAME(XvImage) *image;
     69 };
     70 
     71 
     72 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
     73 
     74 #ifndef NO_SHARED_MEMORY
     75 /* Shared memory error handler routine */
     76 static int shm_error;
     77 static int shm_errhandler(Display *d, XErrorEvent *e)
     78 {
     79         if ( e->error_code == BadAccess ) {
     80         	shm_error = True;
     81         	return(0);
     82         } else
     83 		return(X_handler(d,e));
     84 }
     85 #endif /* !NO_SHARED_MEMORY */
     86 
     87 static int xv_error;
     88 static int xv_errhandler(Display *d, XErrorEvent *e)
     89 {
     90         if ( e->error_code == BadMatch ) {
     91         	xv_error = True;
     92         	return(0);
     93         } else
     94 		return(X_handler(d,e));
     95 }
     96 
     97 #ifdef INTEL_XV_BADALLOC_WORKAROUND
     98 static int intel_errhandler(Display *d, XErrorEvent *e)
     99 {
    100         if ( e->error_code == BadAlloc ) {
    101         	xv_error = True;
    102         	return(0);
    103         } else
    104 		return(X_handler(d,e));
    105 }
    106 
    107 static void X11_ClearYUVOverlay(SDL_Overlay *overlay)
    108 {
    109 	int x,y;
    110 
    111 	switch (overlay->format)
    112 	{
    113 	case SDL_YV12_OVERLAY:
    114 	case SDL_IYUV_OVERLAY:
    115 		for (y = 0; y < overlay->h; y++)
    116 			memset(overlay->pixels[0] + y * overlay->pitches[0],
    117 				0, overlay->w);
    118 
    119 		for (y = 0; y < (overlay->h / 2); y++)
    120 		{
    121 			memset(overlay->pixels[1] + y * overlay->pitches[1],
    122 				-128, overlay->w / 2);
    123 			memset(overlay->pixels[2] + y * overlay->pitches[2],
    124 				-128, overlay->w / 2);
    125 		}
    126 		break;
    127 	case SDL_YUY2_OVERLAY:
    128 	case SDL_YVYU_OVERLAY:
    129 		for (y = 0; y < overlay->h; y++)
    130 		{
    131 			for (x = 0; x < overlay->w; x += 2)
    132 			{
    133 				Uint8 *pixel_pair = overlay->pixels[0] +
    134 					y * overlay->pitches[0] + x * 2;
    135 				pixel_pair[0] = 0;
    136 				pixel_pair[1] = -128;
    137 				pixel_pair[2] = 0;
    138 				pixel_pair[3] = -128;
    139 			}
    140 		}
    141 		break;
    142 	case SDL_UYVY_OVERLAY:
    143 		for (y = 0; y < overlay->h; y++)
    144 		{
    145 			for (x = 0; x < overlay->w; x += 2)
    146 			{
    147 				Uint8 *pixel_pair = overlay->pixels[0] +
    148 					y * overlay->pitches[0] + x * 2;
    149 				pixel_pair[0] = -128;
    150 				pixel_pair[1] = 0;
    151 				pixel_pair[2] = -128;
    152 				pixel_pair[3] = 0;
    153 			}
    154 		}
    155 		break;
    156 	}
    157 }
    158 #endif
    159 
    160 SDL_Overlay *X11_CreateYUVOverlay(_THIS, int width, int height, Uint32 format, SDL_Surface *display)
    161 {
    162 	SDL_Overlay *overlay;
    163 	struct private_yuvhwdata *hwdata;
    164 	int xv_port;
    165 	unsigned int i, j, k;
    166 	unsigned int adaptors;
    167 	SDL_NAME(XvAdaptorInfo) *ainfo;
    168 	int bpp;
    169 #ifndef NO_SHARED_MEMORY
    170 	XShmSegmentInfo *yuvshm;
    171 #endif
    172 #ifdef INTEL_XV_BADALLOC_WORKAROUND
    173 	int intel_adapter = False;
    174 #endif
    175 
    176 	/* Look for the XVideo extension with a valid port for this format */
    177 	xv_port = -1;
    178 	if ( (Success == SDL_NAME(XvQueryExtension)(GFX_Display, &j, &j, &j, &j, &j)) &&
    179 	     (Success == SDL_NAME(XvQueryAdaptors)(GFX_Display,
    180 	                                 RootWindow(GFX_Display, SDL_Screen),
    181 	                                 &adaptors, &ainfo)) ) {
    182 #ifdef USE_LAST_ADAPTOR
    183 		for ( i=0; i < adaptors; ++i )
    184 #else
    185 		for ( i=0; (i < adaptors) && (xv_port == -1); ++i )
    186 #endif /* USE_LAST_ADAPTOR */
    187 		{
    188 			/* Check to see if the visual can be used */
    189 			if ( BUGGY_XFREE86(<=, 4001) ) {
    190 				int visual_ok = 0;
    191 				for ( j=0; j<ainfo[i].num_formats; ++j ) {
    192 					if ( ainfo[i].formats[j].visual_id ==
    193 							SDL_Visual->visualid ) {
    194 						visual_ok = 1;
    195 						break;
    196 					}
    197 				}
    198 				if ( ! visual_ok ) {
    199 					continue;
    200 				}
    201 			}
    202 #ifdef INTEL_XV_BADALLOC_WORKAROUND
    203 			if ( !strcmp(ainfo[i].name, "Intel(R) Video Overla"))
    204 				intel_adapter = True;
    205 			else
    206 				intel_adapter = False;
    207 #endif
    208 			if ( (ainfo[i].type & XvInputMask) &&
    209 			     (ainfo[i].type & XvImageMask) ) {
    210 				int num_formats;
    211 				SDL_NAME(XvImageFormatValues) *formats;
    212 				formats = SDL_NAME(XvListImageFormats)(GFX_Display,
    213 				              ainfo[i].base_id, &num_formats);
    214 #ifdef USE_LAST_ADAPTOR
    215 				for ( j=0; j < num_formats; ++j )
    216 #else
    217 				for ( j=0; (j < num_formats) && (xv_port == -1); ++j )
    218 #endif /* USE_LAST_ADAPTOR */
    219 				{
    220 					if ( (Uint32)formats[j].id == format ) {
    221 						for ( k=0; k < ainfo[i].num_ports; ++k ) {
    222 							if ( Success == SDL_NAME(XvGrabPort)(GFX_Display, ainfo[i].base_id+k, CurrentTime) ) {
    223 								xv_port = ainfo[i].base_id+k;
    224 								break;
    225 							}
    226 						}
    227 					}
    228 				}
    229 				if ( formats ) {
    230 					XFree(formats);
    231 				}
    232 			}
    233 		}
    234 		SDL_NAME(XvFreeAdaptorInfo)(ainfo);
    235 	}
    236 
    237 	/* Precalculate the bpp for the pitch workaround below */
    238 	switch (format) {
    239 	    /* Add any other cases we need to support... */
    240 	    case SDL_YUY2_OVERLAY:
    241 	    case SDL_UYVY_OVERLAY:
    242 	    case SDL_YVYU_OVERLAY:
    243 		bpp = 2;
    244 		break;
    245 	    default:
    246 		bpp = 1;
    247 		break;
    248 	}
    249 
    250 #if 0
    251     /*
    252      * !!! FIXME:
    253      * "Here are some diffs for X11 and yuv.  Note that the last part 2nd
    254      *  diff should probably be a new call to XvQueryAdaptorFree with ainfo
    255      *  and the number of adaptors, instead of the loop through like I did."
    256      *
    257      *  ACHTUNG: This is broken! It looks like XvFreeAdaptorInfo does this
    258      *  for you, so we end up with a double-free. I need to look at this
    259      *  more closely...  --ryan.
    260      */
    261  	for ( i=0; i < adaptors; ++i ) {
    262  	  if (ainfo[i].name != NULL) Xfree(ainfo[i].name);
    263  	  if (ainfo[i].formats != NULL) Xfree(ainfo[i].formats);
    264    	}
    265  	Xfree(ainfo);
    266 #endif
    267 
    268 	if ( xv_port == -1 ) {
    269 		SDL_SetError("No available video ports for requested format");
    270 		return(NULL);
    271 	}
    272 
    273 	/* Enable auto-painting of the overlay colorkey */
    274 	{
    275 		static const char *attr[] = { "XV_AUTOPAINT_COLORKEY", "XV_AUTOPAINT_COLOURKEY" };
    276 		unsigned int i;
    277 
    278 		SDL_NAME(XvSelectPortNotify)(GFX_Display, xv_port, True);
    279 		X_handler = XSetErrorHandler(xv_errhandler);
    280 		for ( i=0; i < sizeof(attr)/(sizeof attr[0]); ++i ) {
    281 			Atom a;
    282 
    283 			xv_error = False;
    284 			a = XInternAtom(GFX_Display, attr[i], True);
    285 			if ( a != None ) {
    286      				SDL_NAME(XvSetPortAttribute)(GFX_Display, xv_port, a, 1);
    287 				XSync(GFX_Display, True);
    288 				if ( ! xv_error ) {
    289 					break;
    290 				}
    291 			}
    292 		}
    293 		XSetErrorHandler(X_handler);
    294 		SDL_NAME(XvSelectPortNotify)(GFX_Display, xv_port, False);
    295 	}
    296 
    297 	/* Create the overlay structure */
    298 	overlay = (SDL_Overlay *)SDL_malloc(sizeof *overlay);
    299 	if ( overlay == NULL ) {
    300 		SDL_NAME(XvUngrabPort)(GFX_Display, xv_port, CurrentTime);
    301 		SDL_OutOfMemory();
    302 		return(NULL);
    303 	}
    304 	SDL_memset(overlay, 0, (sizeof *overlay));
    305 
    306 	/* Fill in the basic members */
    307 	overlay->format = format;
    308 	overlay->w = width;
    309 	overlay->h = height;
    310 
    311 	/* Set up the YUV surface function structure */
    312 	overlay->hwfuncs = &x11_yuvfuncs;
    313 	overlay->hw_overlay = 1;
    314 
    315 	/* Create the pixel data and lookup tables */
    316 	hwdata = (struct private_yuvhwdata *)SDL_malloc(sizeof *hwdata);
    317 	overlay->hwdata = hwdata;
    318 	if ( hwdata == NULL ) {
    319 		SDL_NAME(XvUngrabPort)(GFX_Display, xv_port, CurrentTime);
    320 		SDL_OutOfMemory();
    321 		SDL_FreeYUVOverlay(overlay);
    322 		return(NULL);
    323 	}
    324 	hwdata->port = xv_port;
    325 #ifndef NO_SHARED_MEMORY
    326 	yuvshm = &hwdata->yuvshm;
    327 	SDL_memset(yuvshm, 0, sizeof(*yuvshm));
    328 	hwdata->image = SDL_NAME(XvShmCreateImage)(GFX_Display, xv_port, format,
    329 						   0, width, height, yuvshm);
    330 #ifdef PITCH_WORKAROUND
    331 	if ( hwdata->image != NULL && hwdata->image->pitches[0] != (width*bpp) ) {
    332 		/* Ajust overlay width according to pitch */
    333 		width = hwdata->image->pitches[0] / bpp;
    334 		XFree(hwdata->image);
    335 		hwdata->image = SDL_NAME(XvShmCreateImage)(GFX_Display, xv_port, format,
    336 							   0, width, height, yuvshm);
    337 	}
    338 #endif /* PITCH_WORKAROUND */
    339 	hwdata->yuv_use_mitshm = (hwdata->image != NULL);
    340 	if ( hwdata->yuv_use_mitshm ) {
    341 		yuvshm->shmid = shmget(IPC_PRIVATE, hwdata->image->data_size,
    342 				       IPC_CREAT | 0777);
    343 		if ( yuvshm->shmid >= 0 ) {
    344 			yuvshm->shmaddr = (char *)shmat(yuvshm->shmid, 0, 0);
    345 			yuvshm->readOnly = False;
    346 			if ( yuvshm->shmaddr != (char *)-1 ) {
    347 				shm_error = False;
    348 				X_handler = XSetErrorHandler(shm_errhandler);
    349 				XShmAttach(GFX_Display, yuvshm);
    350 				XSync(GFX_Display, True);
    351 				XSetErrorHandler(X_handler);
    352 				if ( shm_error )
    353 					shmdt(yuvshm->shmaddr);
    354 			} else {
    355 				shm_error = True;
    356 			}
    357 			shmctl(yuvshm->shmid, IPC_RMID, NULL);
    358 		} else {
    359 			shm_error = True;
    360 		}
    361 		if ( shm_error ) {
    362 			XFree(hwdata->image);
    363 			hwdata->yuv_use_mitshm = 0;
    364 		} else {
    365 			hwdata->image->data = yuvshm->shmaddr;
    366 		}
    367 	}
    368 	if ( !hwdata->yuv_use_mitshm )
    369 #endif /* NO_SHARED_MEMORY */
    370 	{
    371 		hwdata->image = SDL_NAME(XvCreateImage)(GFX_Display, xv_port, format,
    372 							0, width, height);
    373 
    374 #ifdef PITCH_WORKAROUND
    375 		if ( hwdata->image != NULL && hwdata->image->pitches[0] != (width*bpp) ) {
    376 			/* Ajust overlay width according to pitch */
    377 			XFree(hwdata->image);
    378 			width = hwdata->image->pitches[0] / bpp;
    379 			hwdata->image = SDL_NAME(XvCreateImage)(GFX_Display, xv_port, format,
    380 								0, width, height);
    381 		}
    382 #endif /* PITCH_WORKAROUND */
    383 		if ( hwdata->image == NULL ) {
    384 			SDL_SetError("Couldn't create XVideo image");
    385 			SDL_FreeYUVOverlay(overlay);
    386 			return(NULL);
    387 		}
    388 		hwdata->image->data = SDL_malloc(hwdata->image->data_size);
    389 		if ( hwdata->image->data == NULL ) {
    390 			SDL_OutOfMemory();
    391 			SDL_FreeYUVOverlay(overlay);
    392 			return(NULL);
    393 		}
    394 	}
    395 
    396 	/* Find the pitch and offset values for the overlay */
    397 	overlay->planes = hwdata->image->num_planes;
    398 	overlay->pitches = (Uint16 *)SDL_malloc(overlay->planes * sizeof(Uint16));
    399 	overlay->pixels = (Uint8 **)SDL_malloc(overlay->planes * sizeof(Uint8 *));
    400 	if ( !overlay->pitches || !overlay->pixels ) {
    401 		SDL_OutOfMemory();
    402 		SDL_FreeYUVOverlay(overlay);
    403 		return(NULL);
    404 	}
    405 	for ( i=0; i<overlay->planes; ++i ) {
    406 		overlay->pitches[i] = hwdata->image->pitches[i];
    407 		overlay->pixels[i] = (Uint8 *)hwdata->image->data +
    408 		                              hwdata->image->offsets[i];
    409 	}
    410 
    411 #ifdef XFREE86_REFRESH_HACK
    412 	/* Work around an XFree86 X server bug (?)
    413 	   We can't perform normal updates in windows that have video
    414 	   being output to them.  See SDL_x11image.c for more details.
    415 	 */
    416 	X11_DisableAutoRefresh(this);
    417 #endif
    418 
    419 #ifdef INTEL_XV_BADALLOC_WORKAROUND
    420 	/* HACK, GRRR sometimes (i810) creating the overlay succeeds, but the
    421 	   first call to XvShm[Put]Image to a mapped window fails with:
    422 	   "BadAlloc (insufficient resources for operation)". This happens with
    423 	   certain formats when the XvImage is too large to the i810's liking.
    424 
    425 	   We work around this by doing a test XvShm[Put]Image with a black
    426 	   Xv image, this may cause some flashing, so only do this check if we
    427 	   are running on an intel Xv-adapter. */
    428 	if (intel_adapter)
    429 	{
    430 		xv_error = False;
    431 		X_handler = XSetErrorHandler(intel_errhandler);
    432 
    433 		X11_ClearYUVOverlay(overlay);
    434 
    435 		/* We set the destination height and width to 1 pixel to avoid
    436 		   putting a large black rectangle over the screen, thus
    437 		   strongly reducing possible flashing. */
    438 #ifndef NO_SHARED_MEMORY
    439 		if ( hwdata->yuv_use_mitshm ) {
    440 			SDL_NAME(XvShmPutImage)(GFX_Display, hwdata->port,
    441 				SDL_Window, SDL_GC,
    442 				hwdata->image,
    443 				0, 0, overlay->w, overlay->h,
    444 				0, 0, 1, 1, False);
    445 		}
    446 		else
    447 #endif
    448 		{
    449 			SDL_NAME(XvPutImage)(GFX_Display, hwdata->port,
    450 				SDL_Window, SDL_GC,
    451 				hwdata->image,
    452 				0, 0, overlay->w, overlay->h,
    453 				0, 0, 1, 1);
    454 		}
    455 		XSync(GFX_Display, False);
    456 		XSetErrorHandler(X_handler);
    457 
    458 		if (xv_error)
    459 		{
    460 			X11_FreeYUVOverlay(this, overlay);
    461 			return NULL;
    462 		}
    463 		/* Repair the (1 pixel worth of) damage we've just done */
    464 		X11_RefreshDisplay(this);
    465 	}
    466 #endif
    467 
    468 	/* We're all done.. */
    469 	return(overlay);
    470 }
    471 
    472 int X11_LockYUVOverlay(_THIS, SDL_Overlay *overlay)
    473 {
    474 	return(0);
    475 }
    476 
    477 void X11_UnlockYUVOverlay(_THIS, SDL_Overlay *overlay)
    478 {
    479 	return;
    480 }
    481 
    482 int X11_DisplayYUVOverlay(_THIS, SDL_Overlay *overlay, SDL_Rect *src, SDL_Rect *dst)
    483 {
    484 	struct private_yuvhwdata *hwdata;
    485 
    486 	hwdata = overlay->hwdata;
    487 
    488 #ifndef NO_SHARED_MEMORY
    489 	if ( hwdata->yuv_use_mitshm ) {
    490 		SDL_NAME(XvShmPutImage)(GFX_Display, hwdata->port, SDL_Window, SDL_GC,
    491 	              hwdata->image,
    492 		      src->x, src->y, src->w, src->h,
    493 		      dst->x, dst->y, dst->w, dst->h, False);
    494 	}
    495 	else
    496 #endif
    497 	{
    498 		SDL_NAME(XvPutImage)(GFX_Display, hwdata->port, SDL_Window, SDL_GC,
    499 				     hwdata->image,
    500 		                     src->x, src->y, src->w, src->h,
    501 		                     dst->x, dst->y, dst->w, dst->h);
    502 	}
    503 	XSync(GFX_Display, False);
    504 	return(0);
    505 }
    506 
    507 void X11_FreeYUVOverlay(_THIS, SDL_Overlay *overlay)
    508 {
    509 	struct private_yuvhwdata *hwdata;
    510 
    511 	hwdata = overlay->hwdata;
    512 	if ( hwdata ) {
    513 		SDL_NAME(XvUngrabPort)(GFX_Display, hwdata->port, CurrentTime);
    514 #ifndef NO_SHARED_MEMORY
    515 		if ( hwdata->yuv_use_mitshm ) {
    516 			XShmDetach(GFX_Display, &hwdata->yuvshm);
    517 			shmdt(hwdata->yuvshm.shmaddr);
    518 		}
    519 #endif
    520 		if ( hwdata->image ) {
    521 			XFree(hwdata->image);
    522 		}
    523 		SDL_free(hwdata);
    524 	}
    525 	if ( overlay->pitches ) {
    526 		SDL_free(overlay->pitches);
    527 		overlay->pitches = NULL;
    528 	}
    529 	if ( overlay->pixels ) {
    530 		SDL_free(overlay->pixels);
    531 		overlay->pixels = NULL;
    532 	}
    533 #ifdef XFREE86_REFRESH_HACK
    534 	X11_EnableAutoRefresh(this);
    535 #endif
    536 }
    537 
    538 #endif /* SDL_VIDEO_DRIVER_X11_XV */
    539