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