src/video/x11/SDL_x11yuv.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:29:03 +0000
branchSDL-1.3
changeset 1663 11775724e3fe
parent 1662 782fd950bd46
child 1668 4da1ee79c9af
permissions -rw-r--r--
fine tuning indent output
     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 #if 0                           /* Apparently the NVidia drivers are fixed */
    49 #define USE_LAST_ADAPTOR
    50 #endif
    51 
    52 /* The functions used to manipulate software video overlays */
    53 static struct private_yuvhwfuncs x11_yuvfuncs = {
    54     X11_LockYUVOverlay,
    55     X11_UnlockYUVOverlay,
    56     X11_DisplayYUVOverlay,
    57     X11_FreeYUVOverlay
    58 };
    59 
    60 struct private_yuvhwdata
    61 {
    62     int port;
    63 #ifndef NO_SHARED_MEMORY
    64     int yuv_use_mitshm;
    65     XShmSegmentInfo yuvshm;
    66 #endif
    67       SDL_NAME (XvImage) * image;
    68 };
    69 
    70 
    71 static int (*X_handler) (Display *, XErrorEvent *) = NULL;
    72 
    73 #ifndef NO_SHARED_MEMORY
    74 /* Shared memory error handler routine */
    75 static int shm_error;
    76 static int
    77 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
    89 xv_errhandler (Display * d, XErrorEvent * e)
    90 {
    91     if (e->error_code == BadMatch) {
    92         xv_error = True;
    93         return (0);
    94     } else
    95         return (X_handler (d, e));
    96 }
    97 
    98 SDL_Overlay *
    99 X11_CreateYUVOverlay (_THIS, int width, int height, Uint32 format,
   100                       SDL_Surface * display)
   101 {
   102     SDL_Overlay *overlay;
   103     struct private_yuvhwdata *hwdata;
   104     int xv_port;
   105     unsigned int i, j, k;
   106     unsigned int adaptors;
   107     SDL_NAME (XvAdaptorInfo) * ainfo;
   108     int bpp;
   109 #ifndef NO_SHARED_MEMORY
   110     XShmSegmentInfo *yuvshm;
   111 #endif
   112 
   113     /* Look for the XVideo extension with a valid port for this format */
   114     xv_port = -1;
   115     if ((Success ==
   116          SDL_NAME (XvQueryExtension) (GFX_Display, &j, &j, &j, &j, &j))
   117         && (Success ==
   118             SDL_NAME (XvQueryAdaptors) (GFX_Display,
   119                                         RootWindow (GFX_Display, SDL_Screen),
   120                                         &adaptors, &ainfo))) {
   121 #ifdef USE_LAST_ADAPTOR
   122         for (i = 0; i < adaptors; ++i)
   123 #else
   124         for (i = 0; (i < adaptors) && (xv_port == -1); ++i)
   125 #endif /* USE_LAST_ADAPTOR */
   126         {
   127             /* Check to see if the visual can be used */
   128             if (BUGGY_XFREE86 (<=, 4001)) {
   129                 int visual_ok = 0;
   130                 for (j = 0; j < ainfo[i].num_formats; ++j) {
   131                     if (ainfo[i].formats[j].visual_id == SDL_Visual->visualid) {
   132                         visual_ok = 1;
   133                         break;
   134                     }
   135                 }
   136                 if (!visual_ok) {
   137                     continue;
   138                 }
   139             }
   140             if ((ainfo[i].type & XvInputMask) &&
   141                 (ainfo[i].type & XvImageMask)) {
   142                 int num_formats;
   143                 SDL_NAME (XvImageFormatValues) * formats;
   144                 formats = SDL_NAME (XvListImageFormats) (GFX_Display,
   145                                                          ainfo[i].
   146                                                          base_id,
   147                                                          &num_formats);
   148 #ifdef USE_LAST_ADAPTOR
   149                 for (j = 0; j < num_formats; ++j)
   150 #else
   151                 for (j = 0; (j < num_formats) && (xv_port == -1); ++j)
   152 #endif /* USE_LAST_ADAPTOR */
   153                 {
   154                     if ((Uint32) formats[j].id == format) {
   155                         for (k = 0; k < ainfo[i].num_ports; ++k) {
   156                             if (Success == SDL_NAME (XvGrabPort)
   157                                 (GFX_Display,
   158                                  ainfo[i].base_id + k, CurrentTime)) {
   159                                 xv_port = ainfo[i].base_id + k;
   160                                 break;
   161                             }
   162                         }
   163                     }
   164                 }
   165                 if (formats) {
   166                     XFree (formats);
   167                 }
   168             }
   169         }
   170         SDL_NAME (XvFreeAdaptorInfo) (ainfo);
   171     }
   172 
   173     /* Precalculate the bpp for the pitch workaround below */
   174     switch (format) {
   175         /* Add any other cases we need to support... */
   176     case SDL_YUY2_OVERLAY:
   177     case SDL_UYVY_OVERLAY:
   178     case SDL_YVYU_OVERLAY:
   179         bpp = 2;
   180         break;
   181     default:
   182         bpp = 1;
   183         break;
   184     }
   185 
   186 #if 0
   187     /*
   188      * !!! FIXME:
   189      * "Here are some diffs for X11 and yuv.  Note that the last part 2nd
   190      *  diff should probably be a new call to XvQueryAdaptorFree with ainfo
   191      *  and the number of adaptors, instead of the loop through like I did."
   192      *
   193      *  ACHTUNG: This is broken! It looks like XvFreeAdaptorInfo does this
   194      *  for you, so we end up with a double-free. I need to look at this
   195      *  more closely...  --ryan.
   196      */
   197     for (i = 0; i < adaptors; ++i) {
   198         if (ainfo[i].name != NULL)
   199             Xfree (ainfo[i].name);
   200         if (ainfo[i].formats != NULL)
   201             Xfree (ainfo[i].formats);
   202     }
   203     Xfree (ainfo);
   204 #endif
   205 
   206     if (xv_port == -1) {
   207         SDL_SetError ("No available video ports for requested format");
   208         return (NULL);
   209     }
   210 
   211     /* Enable auto-painting of the overlay colorkey */
   212     {
   213         static const char *attr[] =
   214             { "XV_AUTOPAINT_COLORKEY", "XV_AUTOPAINT_COLOURKEY" };
   215         unsigned int i;
   216 
   217         SDL_NAME (XvSelectPortNotify) (GFX_Display, xv_port, True);
   218         X_handler = XSetErrorHandler (xv_errhandler);
   219         for (i = 0; i < sizeof (attr) / (sizeof attr[0]); ++i) {
   220             Atom a;
   221 
   222             xv_error = False;
   223             a = XInternAtom (GFX_Display, attr[i], True);
   224             if (a != None) {
   225                 SDL_NAME (XvSetPortAttribute) (GFX_Display, xv_port, a, 1);
   226                 XSync (GFX_Display, True);
   227                 if (!xv_error) {
   228                     break;
   229                 }
   230             }
   231         }
   232         XSetErrorHandler (X_handler);
   233         SDL_NAME (XvSelectPortNotify) (GFX_Display, xv_port, False);
   234     }
   235 
   236     /* Create the overlay structure */
   237     overlay = (SDL_Overlay *) SDL_malloc (sizeof *overlay);
   238     if (overlay == NULL) {
   239         SDL_NAME (XvUngrabPort) (GFX_Display, xv_port, CurrentTime);
   240         SDL_OutOfMemory ();
   241         return (NULL);
   242     }
   243     SDL_memset (overlay, 0, (sizeof *overlay));
   244 
   245     /* Fill in the basic members */
   246     overlay->format = format;
   247     overlay->w = width;
   248     overlay->h = height;
   249 
   250     /* Set up the YUV surface function structure */
   251     overlay->hwfuncs = &x11_yuvfuncs;
   252     overlay->hw_overlay = 1;
   253 
   254     /* Create the pixel data and lookup tables */
   255     hwdata = (struct private_yuvhwdata *) SDL_malloc (sizeof *hwdata);
   256     overlay->hwdata = hwdata;
   257     if (hwdata == NULL) {
   258         SDL_NAME (XvUngrabPort) (GFX_Display, xv_port, CurrentTime);
   259         SDL_OutOfMemory ();
   260         SDL_FreeYUVOverlay (overlay);
   261         return (NULL);
   262     }
   263     hwdata->port = xv_port;
   264 #ifndef NO_SHARED_MEMORY
   265     yuvshm = &hwdata->yuvshm;
   266     SDL_memset (yuvshm, 0, sizeof (*yuvshm));
   267     hwdata->image = SDL_NAME (XvShmCreateImage) (GFX_Display, xv_port, format,
   268                                                  0, width, height, yuvshm);
   269 #ifdef PITCH_WORKAROUND
   270     if (hwdata->image != NULL && hwdata->image->pitches[0] != (width * bpp)) {
   271         /* Ajust overlay width according to pitch */
   272         XFree (hwdata->image);
   273         width = hwdata->image->pitches[0] / bpp;
   274         hwdata->image =
   275             SDL_NAME (XvShmCreateImage) (GFX_Display, xv_port, format, 0,
   276                                          width, height, yuvshm);
   277     }
   278 #endif /* PITCH_WORKAROUND */
   279     hwdata->yuv_use_mitshm = (hwdata->image != NULL);
   280     if (hwdata->yuv_use_mitshm) {
   281         yuvshm->shmid = shmget (IPC_PRIVATE, hwdata->image->data_size,
   282                                 IPC_CREAT | 0777);
   283         if (yuvshm->shmid >= 0) {
   284             yuvshm->shmaddr = (char *) shmat (yuvshm->shmid, 0, 0);
   285             yuvshm->readOnly = False;
   286             if (yuvshm->shmaddr != (char *) -1) {
   287                 shm_error = False;
   288                 X_handler = XSetErrorHandler (shm_errhandler);
   289                 XShmAttach (GFX_Display, yuvshm);
   290                 XSync (GFX_Display, True);
   291                 XSetErrorHandler (X_handler);
   292                 if (shm_error)
   293                     shmdt (yuvshm->shmaddr);
   294             } else {
   295                 shm_error = True;
   296             }
   297             shmctl (yuvshm->shmid, IPC_RMID, NULL);
   298         } else {
   299             shm_error = True;
   300         }
   301         if (shm_error) {
   302             XFree (hwdata->image);
   303             hwdata->yuv_use_mitshm = 0;
   304         } else {
   305             hwdata->image->data = yuvshm->shmaddr;
   306         }
   307     }
   308     if (!hwdata->yuv_use_mitshm)
   309 #endif /* NO_SHARED_MEMORY */
   310     {
   311         hwdata->image =
   312             SDL_NAME (XvCreateImage) (GFX_Display, xv_port, format, 0,
   313                                       width, height);
   314 
   315 #ifdef PITCH_WORKAROUND
   316         if (hwdata->image != NULL
   317             && hwdata->image->pitches[0] != (width * bpp)) {
   318             /* Ajust overlay width according to pitch */
   319             XFree (hwdata->image);
   320             width = hwdata->image->pitches[0] / bpp;
   321             hwdata->image =
   322                 SDL_NAME (XvCreateImage) (GFX_Display, xv_port, format, 0,
   323                                           width, height);
   324         }
   325 #endif /* PITCH_WORKAROUND */
   326         if (hwdata->image == NULL) {
   327             SDL_SetError ("Couldn't create XVideo image");
   328             SDL_FreeYUVOverlay (overlay);
   329             return (NULL);
   330         }
   331         hwdata->image->data = SDL_malloc (hwdata->image->data_size);
   332         if (hwdata->image->data == NULL) {
   333             SDL_OutOfMemory ();
   334             SDL_FreeYUVOverlay (overlay);
   335             return (NULL);
   336         }
   337     }
   338 
   339     /* Find the pitch and offset values for the overlay */
   340     overlay->planes = hwdata->image->num_planes;
   341     overlay->pitches =
   342         (Uint16 *) SDL_malloc (overlay->planes * sizeof (Uint16));
   343     overlay->pixels =
   344         (Uint8 **) SDL_malloc (overlay->planes * sizeof (Uint8 *));
   345     if (!overlay->pitches || !overlay->pixels) {
   346         SDL_OutOfMemory ();
   347         SDL_FreeYUVOverlay (overlay);
   348         return (NULL);
   349     }
   350     for (i = 0; i < overlay->planes; ++i) {
   351         overlay->pitches[i] = hwdata->image->pitches[i];
   352         overlay->pixels[i] = (Uint8 *) hwdata->image->data +
   353             hwdata->image->offsets[i];
   354     }
   355 
   356 #ifdef XFREE86_REFRESH_HACK
   357     /* Work around an XFree86 X server bug (?)
   358        We can't perform normal updates in windows that have video
   359        being output to them.  See SDL_x11image.c for more details.
   360      */
   361     X11_DisableAutoRefresh (this);
   362 #endif
   363 
   364     /* We're all done.. */
   365     return (overlay);
   366 }
   367 
   368 int
   369 X11_LockYUVOverlay (_THIS, SDL_Overlay * overlay)
   370 {
   371     return (0);
   372 }
   373 
   374 void
   375 X11_UnlockYUVOverlay (_THIS, SDL_Overlay * overlay)
   376 {
   377     return;
   378 }
   379 
   380 int
   381 X11_DisplayYUVOverlay (_THIS, SDL_Overlay * overlay, SDL_Rect * src,
   382                        SDL_Rect * dst)
   383 {
   384     struct private_yuvhwdata *hwdata;
   385 
   386     hwdata = overlay->hwdata;
   387 
   388 #ifndef NO_SHARED_MEMORY
   389     if (hwdata->yuv_use_mitshm) {
   390         SDL_NAME (XvShmPutImage) (GFX_Display, hwdata->port, SDL_Window,
   391                                   SDL_GC, hwdata->image, src->x, src->y,
   392                                   src->w, src->h, dst->x, dst->y, dst->w,
   393                                   dst->h, False);
   394     } else
   395 #endif
   396     {
   397         SDL_NAME (XvPutImage) (GFX_Display, hwdata->port, SDL_Window,
   398                                SDL_GC, hwdata->image, src->x, src->y,
   399                                src->w, src->h, dst->x, dst->y, dst->w,
   400                                dst->h);
   401     }
   402     XSync (GFX_Display, False);
   403     return (0);
   404 }
   405 
   406 void
   407 X11_FreeYUVOverlay (_THIS, SDL_Overlay * overlay)
   408 {
   409     struct private_yuvhwdata *hwdata;
   410 
   411     hwdata = overlay->hwdata;
   412     if (hwdata) {
   413         SDL_NAME (XvUngrabPort) (GFX_Display, hwdata->port, CurrentTime);
   414 #ifndef NO_SHARED_MEMORY
   415         if (hwdata->yuv_use_mitshm) {
   416             XShmDetach (GFX_Display, &hwdata->yuvshm);
   417             shmdt (hwdata->yuvshm.shmaddr);
   418         }
   419 #endif
   420         if (hwdata->image) {
   421             XFree (hwdata->image);
   422         }
   423         SDL_free (hwdata);
   424     }
   425     if (overlay->pitches) {
   426         SDL_free (overlay->pitches);
   427         overlay->pitches = NULL;
   428     }
   429     if (overlay->pixels) {
   430         SDL_free (overlay->pixels);
   431         overlay->pixels = NULL;
   432     }
   433 #ifdef XFREE86_REFRESH_HACK
   434     X11_EnableAutoRefresh (this);
   435 #endif
   436 }
   437 
   438 #endif /* SDL_VIDEO_DRIVER_X11_XV */
   439 /* vi: set ts=4 sw=4 expandtab: */