src/video/x11/SDL_x11yuv.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 22 Mar 2006 05:00:59 +0000
changeset 1575 3ba88cb7eb1b
parent 1402 d910939febfa
child 1642 f1211a4b7380
permissions -rw-r--r--
Updated dynamic X11 code. See details in Bugzilla #170.
     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@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 *dstrect)
   358 {
   359 	struct private_yuvhwdata *hwdata;
   360 
   361 	hwdata = overlay->hwdata;
   362 #ifndef NO_SHARED_MEMORY
   363 	if ( hwdata->yuv_use_mitshm ) {
   364 		SDL_NAME(XvShmPutImage)(GFX_Display, hwdata->port, SDL_Window, SDL_GC,
   365 	              hwdata->image, 0, 0, overlay->w, overlay->h,
   366 	              dstrect->x, dstrect->y, dstrect->w, dstrect->h, False);
   367 	}
   368 	else
   369 #endif
   370 	{
   371 		SDL_NAME(XvPutImage)(GFX_Display, hwdata->port, SDL_Window, SDL_GC,
   372 				     hwdata->image, 0, 0, overlay->w, overlay->h,
   373 				     dstrect->x, dstrect->y, dstrect->w, dstrect->h);
   374 	}
   375 	XSync(GFX_Display, False);
   376 	return(0);
   377 }
   378 
   379 void X11_FreeYUVOverlay(_THIS, SDL_Overlay *overlay)
   380 {
   381 	struct private_yuvhwdata *hwdata;
   382 
   383 	hwdata = overlay->hwdata;
   384 	if ( hwdata ) {
   385 		SDL_NAME(XvUngrabPort)(GFX_Display, hwdata->port, CurrentTime);
   386 #ifndef NO_SHARED_MEMORY
   387 		if ( hwdata->yuv_use_mitshm ) {
   388 			XShmDetach(GFX_Display, &hwdata->yuvshm);
   389 			shmdt(hwdata->yuvshm.shmaddr);
   390 		}
   391 #endif
   392 		if ( hwdata->image ) {
   393 			XFree(hwdata->image);
   394 		}
   395 		SDL_free(hwdata);
   396 	}
   397 	if ( overlay->pitches ) {
   398 		SDL_free(overlay->pitches);
   399 		overlay->pitches = NULL;
   400 	}
   401 	if ( overlay->pixels ) {
   402 		SDL_free(overlay->pixels);
   403 		overlay->pixels = NULL;
   404 	}
   405 #ifdef XFREE86_REFRESH_HACK
   406 	X11_EnableAutoRefresh(this);
   407 #endif
   408 }
   409 
   410 #endif /* SDL_VIDEO_DRIVER_X11_XV */