view src/video/x11/SDL_x11yuv.c @ 1944:91f41fc124a7

The AAlib driver is fun, but not worth rewriting for SDL 1.3
author Sam Lantinga <slouken@libsdl.org>
date Wed, 26 Jul 2006 03:24:27 +0000
parents c121d94672cb
children
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

/* This is the XFree86 Xv extension implementation of YUV video overlays */

#if SDL_VIDEO_DRIVER_X11_XV

#include <X11/Xlib.h>
#ifndef NO_SHARED_MEMORY
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#endif
#include "../Xext/extensions/Xvlib.h"

#include "SDL_x11yuv_c.h"
#include "../SDL_yuvfuncs.h"

#define XFREE86_REFRESH_HACK
#ifdef XFREE86_REFRESH_HACK
#include "SDL_x11image_c.h"
#endif

/* Workaround when pitch != width */
#define PITCH_WORKAROUND

/* Fix for the NVidia GeForce 2 - use the last available adaptor */
#if 0                           /* Apparently the NVidia drivers are fixed */
#define USE_LAST_ADAPTOR
#endif

/* The functions used to manipulate software video overlays */
static struct private_yuvhwfuncs x11_yuvfuncs = {
    X11_LockYUVOverlay,
    X11_UnlockYUVOverlay,
    X11_DisplayYUVOverlay,
    X11_FreeYUVOverlay
};

struct private_yuvhwdata
{
    int port;
#ifndef NO_SHARED_MEMORY
    int yuv_use_mitshm;
    XShmSegmentInfo yuvshm;
#endif
      SDL_NAME(XvImage) * image;
};


static int (*X_handler) (Display *, XErrorEvent *) = NULL;

#ifndef NO_SHARED_MEMORY
/* Shared memory error handler routine */
static int shm_error;
static int
shm_errhandler(Display * d, XErrorEvent * e)
{
    if (e->error_code == BadAccess) {
        shm_error = True;
        return (0);
    } else
        return (X_handler(d, e));
}
#endif /* !NO_SHARED_MEMORY */

static int xv_error;
static int
xv_errhandler(Display * d, XErrorEvent * e)
{
    if (e->error_code == BadMatch) {
        xv_error = True;
        return (0);
    } else
        return (X_handler(d, e));
}

SDL_Overlay *
X11_CreateYUVOverlay(_THIS, int width, int height, Uint32 format,
                     SDL_Surface * display)
{
    SDL_Overlay *overlay;
    struct private_yuvhwdata *hwdata;
    int xv_port;
    unsigned int i, j, k;
    unsigned int adaptors;
    SDL_NAME(XvAdaptorInfo) * ainfo;
    int bpp;
#ifndef NO_SHARED_MEMORY
    XShmSegmentInfo *yuvshm;
#endif

    /* Look for the XVideo extension with a valid port for this format */
    xv_port = -1;
    if ((Success ==
         SDL_NAME(XvQueryExtension) (GFX_Display, &j, &j, &j, &j, &j))
        && (Success ==
            SDL_NAME(XvQueryAdaptors) (GFX_Display,
                                       RootWindow(GFX_Display, SDL_Screen),
                                       &adaptors, &ainfo))) {
#ifdef USE_LAST_ADAPTOR
        for (i = 0; i < adaptors; ++i)
#else
        for (i = 0; (i < adaptors) && (xv_port == -1); ++i)
#endif /* USE_LAST_ADAPTOR */
        {
            /* Check to see if the visual can be used */
            if (BUGGY_XFREE86(<=, 4001)) {
                int visual_ok = 0;
                for (j = 0; j < ainfo[i].num_formats; ++j) {
                    if (ainfo[i].formats[j].visual_id == SDL_Visual->visualid) {
                        visual_ok = 1;
                        break;
                    }
                }
                if (!visual_ok) {
                    continue;
                }
            }
            if ((ainfo[i].type & XvInputMask) &&
                (ainfo[i].type & XvImageMask)) {
                int num_formats;
                SDL_NAME(XvImageFormatValues) * formats;
                formats = SDL_NAME(XvListImageFormats) (GFX_Display,
                                                        ainfo[i].
                                                        base_id,
                                                        &num_formats);
#ifdef USE_LAST_ADAPTOR
                for (j = 0; j < num_formats; ++j)
#else
                for (j = 0; (j < num_formats) && (xv_port == -1); ++j)
#endif /* USE_LAST_ADAPTOR */
                {
                    if ((Uint32) formats[j].id == format) {
                        for (k = 0; k < ainfo[i].num_ports; ++k) {
                            if (Success == SDL_NAME(XvGrabPort)
                                (GFX_Display,
                                 ainfo[i].base_id + k, CurrentTime)) {
                                xv_port = ainfo[i].base_id + k;
                                break;
                            }
                        }
                    }
                }
                if (formats) {
                    XFree(formats);
                }
            }
        }
        SDL_NAME(XvFreeAdaptorInfo) (ainfo);
    }

    /* Precalculate the bpp for the pitch workaround below */
    switch (format) {
        /* Add any other cases we need to support... */
    case SDL_YUY2_OVERLAY:
    case SDL_UYVY_OVERLAY:
    case SDL_YVYU_OVERLAY:
        bpp = 2;
        break;
    default:
        bpp = 1;
        break;
    }

#if 0
    /*
     * !!! FIXME:
     * "Here are some diffs for X11 and yuv.  Note that the last part 2nd
     *  diff should probably be a new call to XvQueryAdaptorFree with ainfo
     *  and the number of adaptors, instead of the loop through like I did."
     *
     *  ACHTUNG: This is broken! It looks like XvFreeAdaptorInfo does this
     *  for you, so we end up with a double-free. I need to look at this
     *  more closely...  --ryan.
     */
    for (i = 0; i < adaptors; ++i) {
        if (ainfo[i].name != NULL)
            Xfree(ainfo[i].name);
        if (ainfo[i].formats != NULL)
            Xfree(ainfo[i].formats);
    }
    Xfree(ainfo);
#endif

    if (xv_port == -1) {
        SDL_SetError("No available video ports for requested format");
        return (NULL);
    }

    /* Enable auto-painting of the overlay colorkey */
    {
        static const char *attr[] =
            { "XV_AUTOPAINT_COLORKEY", "XV_AUTOPAINT_COLOURKEY" };
        unsigned int i;

        SDL_NAME(XvSelectPortNotify) (GFX_Display, xv_port, True);
        X_handler = XSetErrorHandler(xv_errhandler);
        for (i = 0; i < sizeof(attr) / (sizeof attr[0]); ++i) {
            Atom a;

            xv_error = False;
            a = XInternAtom(GFX_Display, attr[i], True);
            if (a != None) {
                SDL_NAME(XvSetPortAttribute) (GFX_Display, xv_port, a, 1);
                XSync(GFX_Display, True);
                if (!xv_error) {
                    break;
                }
            }
        }
        XSetErrorHandler(X_handler);
        SDL_NAME(XvSelectPortNotify) (GFX_Display, xv_port, False);
    }

    /* Create the overlay structure */
    overlay = (SDL_Overlay *) SDL_malloc(sizeof *overlay);
    if (overlay == NULL) {
        SDL_NAME(XvUngrabPort) (GFX_Display, xv_port, CurrentTime);
        SDL_OutOfMemory();
        return (NULL);
    }
    SDL_memset(overlay, 0, (sizeof *overlay));

    /* Fill in the basic members */
    overlay->format = format;
    overlay->w = width;
    overlay->h = height;

    /* Set up the YUV surface function structure */
    overlay->hwfuncs = &x11_yuvfuncs;
    overlay->hw_overlay = 1;

    /* Create the pixel data and lookup tables */
    hwdata = (struct private_yuvhwdata *) SDL_malloc(sizeof *hwdata);
    overlay->hwdata = hwdata;
    if (hwdata == NULL) {
        SDL_NAME(XvUngrabPort) (GFX_Display, xv_port, CurrentTime);
        SDL_OutOfMemory();
        SDL_FreeYUVOverlay(overlay);
        return (NULL);
    }
    hwdata->port = xv_port;
#ifndef NO_SHARED_MEMORY
    yuvshm = &hwdata->yuvshm;
    SDL_memset(yuvshm, 0, sizeof(*yuvshm));
    hwdata->image = SDL_NAME(XvShmCreateImage) (GFX_Display, xv_port, format,
                                                0, width, height, yuvshm);
#ifdef PITCH_WORKAROUND
    if (hwdata->image != NULL && hwdata->image->pitches[0] != (width * bpp)) {
        /* Ajust overlay width according to pitch */
        XFree(hwdata->image);
        width = hwdata->image->pitches[0] / bpp;
        hwdata->image =
            SDL_NAME(XvShmCreateImage) (GFX_Display, xv_port, format, 0,
                                        width, height, yuvshm);
    }
#endif /* PITCH_WORKAROUND */
    hwdata->yuv_use_mitshm = (hwdata->image != NULL);
    if (hwdata->yuv_use_mitshm) {
        yuvshm->shmid = shmget(IPC_PRIVATE, hwdata->image->data_size,
                               IPC_CREAT | 0777);
        if (yuvshm->shmid >= 0) {
            yuvshm->shmaddr = (char *) shmat(yuvshm->shmid, 0, 0);
            yuvshm->readOnly = False;
            if (yuvshm->shmaddr != (char *) -1) {
                shm_error = False;
                X_handler = XSetErrorHandler(shm_errhandler);
                XShmAttach(GFX_Display, yuvshm);
                XSync(GFX_Display, True);
                XSetErrorHandler(X_handler);
                if (shm_error)
                    shmdt(yuvshm->shmaddr);
            } else {
                shm_error = True;
            }
            shmctl(yuvshm->shmid, IPC_RMID, NULL);
        } else {
            shm_error = True;
        }
        if (shm_error) {
            XFree(hwdata->image);
            hwdata->yuv_use_mitshm = 0;
        } else {
            hwdata->image->data = yuvshm->shmaddr;
        }
    }
    if (!hwdata->yuv_use_mitshm)
#endif /* NO_SHARED_MEMORY */
    {
        hwdata->image =
            SDL_NAME(XvCreateImage) (GFX_Display, xv_port, format, 0,
                                     width, height);

#ifdef PITCH_WORKAROUND
        if (hwdata->image != NULL
            && hwdata->image->pitches[0] != (width * bpp)) {
            /* Ajust overlay width according to pitch */
            XFree(hwdata->image);
            width = hwdata->image->pitches[0] / bpp;
            hwdata->image =
                SDL_NAME(XvCreateImage) (GFX_Display, xv_port, format, 0,
                                         width, height);
        }
#endif /* PITCH_WORKAROUND */
        if (hwdata->image == NULL) {
            SDL_SetError("Couldn't create XVideo image");
            SDL_FreeYUVOverlay(overlay);
            return (NULL);
        }
        hwdata->image->data = SDL_malloc(hwdata->image->data_size);
        if (hwdata->image->data == NULL) {
            SDL_OutOfMemory();
            SDL_FreeYUVOverlay(overlay);
            return (NULL);
        }
    }

    /* Find the pitch and offset values for the overlay */
    overlay->planes = hwdata->image->num_planes;
    overlay->pitches =
        (Uint16 *) SDL_malloc(overlay->planes * sizeof(Uint16));
    overlay->pixels =
        (Uint8 **) SDL_malloc(overlay->planes * sizeof(Uint8 *));
    if (!overlay->pitches || !overlay->pixels) {
        SDL_OutOfMemory();
        SDL_FreeYUVOverlay(overlay);
        return (NULL);
    }
    for (i = 0; i < overlay->planes; ++i) {
        overlay->pitches[i] = hwdata->image->pitches[i];
        overlay->pixels[i] = (Uint8 *) hwdata->image->data +
            hwdata->image->offsets[i];
    }

#ifdef XFREE86_REFRESH_HACK
    /* Work around an XFree86 X server bug (?)
       We can't perform normal updates in windows that have video
       being output to them.  See SDL_x11image.c for more details.
     */
    X11_DisableAutoRefresh(this);
#endif

    /* We're all done.. */
    return (overlay);
}

int
X11_LockYUVOverlay(_THIS, SDL_Overlay * overlay)
{
    return (0);
}

void
X11_UnlockYUVOverlay(_THIS, SDL_Overlay * overlay)
{
    return;
}

int
X11_DisplayYUVOverlay(_THIS, SDL_Overlay * overlay, SDL_Rect * src,
                      SDL_Rect * dst)
{
    struct private_yuvhwdata *hwdata;

    hwdata = overlay->hwdata;

#ifndef NO_SHARED_MEMORY
    if (hwdata->yuv_use_mitshm) {
        SDL_NAME(XvShmPutImage) (GFX_Display, hwdata->port, SDL_Window,
                                 SDL_GC, hwdata->image, src->x, src->y,
                                 src->w, src->h, dst->x, dst->y, dst->w,
                                 dst->h, False);
    } else
#endif
    {
        SDL_NAME(XvPutImage) (GFX_Display, hwdata->port, SDL_Window,
                              SDL_GC, hwdata->image, src->x, src->y,
                              src->w, src->h, dst->x, dst->y, dst->w, dst->h);
    }
    XSync(GFX_Display, False);
    return (0);
}

void
X11_FreeYUVOverlay(_THIS, SDL_Overlay * overlay)
{
    struct private_yuvhwdata *hwdata;

    hwdata = overlay->hwdata;
    if (hwdata) {
        SDL_NAME(XvUngrabPort) (GFX_Display, hwdata->port, CurrentTime);
#ifndef NO_SHARED_MEMORY
        if (hwdata->yuv_use_mitshm) {
            XShmDetach(GFX_Display, &hwdata->yuvshm);
            shmdt(hwdata->yuvshm.shmaddr);
        }
#endif
        if (hwdata->image) {
            XFree(hwdata->image);
        }
        SDL_free(hwdata);
    }
    if (overlay->pitches) {
        SDL_free(overlay->pitches);
        overlay->pitches = NULL;
    }
    if (overlay->pixels) {
        SDL_free(overlay->pixels);
        overlay->pixels = NULL;
    }
#ifdef XFREE86_REFRESH_HACK
    X11_EnableAutoRefresh(this);
#endif
}

#endif /* SDL_VIDEO_DRIVER_X11_XV */
/* vi: set ts=4 sw=4 expandtab: */