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