Logo Search packages:      
Sourcecode: xawtv version File versions  Download package

xv.c

/*
 * (most) Xvideo extention code is here.
 *
 * (c) 2001 Gerd Knorr <kraxel@bytesex.org>
 */

#include "config.h"

#ifdef HAVE_LIBXV

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/extensions/XShm.h>
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>

#include "grab-ng.h"
#include "commands.h"    /* FIXME: global *drv vars */
#include "atoms.h"
#include "xv.h"

extern Display    *dpy;
int               have_xv;

const struct ng_vid_driver xv_driver;

static int              ver, rel, req, ev, err, grabbed;
static int              adaptors;
static int              attributes;
static XvAdaptorInfo        *ai;
static XvEncodingInfo       *ei;
static XvAttribute          *at;

static int
xv_overlay(void *handle, struct ng_video_fmt *fmt, int x, int y,
         struct OVERLAY_CLIP *oc, int count, int aspect)
{
    if (debug)
      fprintf(stderr,"Ouch: xv_overlay called\n");
    return 0;
}

/* ********************************************************************* */

struct ENC_MAP {
    int norm;
    int input;
    int encoding;
};

struct xv_handle {
    /* port */
    int                  vi_adaptor;
    XvPortID             vi_port;
    GC                   vi_gc;
    
    /* attributes */
    int                  nattr;
    struct ng_attribute  *attr;
    Atom xv_encoding;
    Atom xv_freq;
    Atom xv_colorkey;

    /* encoding */
    struct ENC_MAP       *enc_map;
    int                  norm, input, enc;
    int                  encodings;
};

static const struct XVATTR {
    int   id;
    int   type;
    char  *atom;
} xvattr[] = {
    { ATTR_ID_COLOR,    ATTR_TYPE_INTEGER, "XV_COLOR"       },
    { ATTR_ID_COLOR,    ATTR_TYPE_INTEGER, "XV_SATURATION"  },
    { ATTR_ID_HUE,      ATTR_TYPE_INTEGER, "XV_HUE",        },
    { ATTR_ID_BRIGHT,   ATTR_TYPE_INTEGER, "XV_BRIGHTNESS", },
    { ATTR_ID_CONTRAST, ATTR_TYPE_INTEGER, "XV_CONTRAST",   },
    { ATTR_ID_MUTE,     ATTR_TYPE_BOOL,    "XV_MUTE",       },
    { ATTR_ID_VOLUME,   ATTR_TYPE_INTEGER, "XV_VOLUME",     },
    { -1,               -1,                "XV_COLORKEY",   },
    { -1,               -1,                "XV_FREQ",       },
    { -1,               -1,                "XV_ENCODING",   },
    {}
};

static int xv_read_attr(struct ng_attribute *attr)
{
    struct xv_handle *h   = attr->handle;
    const XvAttribute *at = attr->priv;
    Atom atom;
    int value = 0;

    if (NULL != at) {
      atom = XInternAtom(dpy, at->name, False);
      XvGetPortAttribute(dpy, h->vi_port,atom,&value);
      if (debug)
          fprintf(stderr,"xv: get %s: %d\n",at->name,value);
      
    } else if (attr->id == ATTR_ID_NORM) {
      value = h->norm;
      
    } else if (attr->id == ATTR_ID_INPUT) {
      value = h->input;

    }
    return value;
}

static void xv_write_attr(struct ng_attribute *attr, int value)
{
    struct xv_handle *h   = attr->handle;
    const XvAttribute *at = attr->priv;
    Atom atom;
    int i;

    if (NULL != at) {
      atom = XInternAtom(dpy, at->name, False);
      XvSetPortAttribute(dpy, h->vi_port,atom,value);
      if (debug)
          fprintf(stderr,"xv: set %s: %d\n",at->name,value);

    } else if (attr->id == ATTR_ID_NORM || attr->id == ATTR_ID_INPUT) {
      if (attr->id == ATTR_ID_NORM)
          h->norm  = value;
      if (attr->id == ATTR_ID_INPUT)
          h->input = value;
      for (i = 0; i < h->encodings; i++) {
          if (h->enc_map[i].norm  == h->norm &&
            h->enc_map[i].input == h->input) {
            h->enc = i;
            XvSetPortAttribute(dpy,h->vi_port,h->xv_encoding,h->enc);
            break;
          }
      }
    }
    /* needed for proper timing on the
       "mute - wait - switch - wait - unmute" channel switches */
    XSync(dpy,False);
}

static void
xv_add_attr(struct xv_handle *h, int id, int type,
          int defval, struct STRTAB *choices, XvAttribute *at)
{
    int i;
    
    h->attr = realloc(h->attr,(h->nattr+2) * sizeof(struct ng_attribute));
    memset(h->attr+h->nattr,0,sizeof(struct ng_attribute)*2);
    if (at) {
      h->attr[h->nattr].priv    = at;
      for (i = 0; xvattr[i].atom != NULL; i++)
          if (0 == strcmp(xvattr[i].atom,at->name))
            break;
      if (-1 == xvattr[i].type)
          /* ignore this one */
          return;
      if (NULL != xvattr[i].atom) {
          h->attr[h->nattr].id      = xvattr[i].id;
          h->attr[h->nattr].type    = xvattr[i].type;
          h->attr[h->nattr].priv    = at;
          if (ATTR_TYPE_INTEGER == h->attr[h->nattr].type) {
            h->attr[h->nattr].min = at->min_value;
            h->attr[h->nattr].max = at->max_value;
          }
      } else {
          /* unknown */
          return;
      }
    }

    if (id)
      h->attr[h->nattr].id      = id;
    if (type)
      h->attr[h->nattr].type    = type;
    if (defval)
      h->attr[h->nattr].defval  = defval;
    if (choices)
      h->attr[h->nattr].choices = choices;
    if (h->attr[h->nattr].id < ATTR_ID_COUNT)
      h->attr[h->nattr].name    = ng_attr_to_desc[h->attr[h->nattr].id];

    h->attr[h->nattr].read    = xv_read_attr;
    h->attr[h->nattr].write   = xv_write_attr;
    h->attr[h->nattr].handle  = h;
    h->nattr++;
}

static unsigned long
xv_getfreq(void *handle)
{
    struct xv_handle *h = handle;
    unsigned int freq;

    XvGetPortAttribute(dpy,h->vi_port,h->xv_freq,&freq);
    return freq;
}

static void
xv_setfreq(void *handle, unsigned long freq)
{
    struct xv_handle *h = handle;

    XvSetPortAttribute(dpy,h->vi_port,h->xv_freq,freq);
    XSync(dpy,False);
}

static int
xv_tuned(void *handle)
{
    /* don't know ... */
    return 0;
}

void
xv_video(Window win, int dw, int dh, int on)
{
    struct xv_handle *h = h_drv; /* FIXME */
    int sx,sy,dx,dy;
    int sw,sh;
    
    if (on) {
      sx = sy = dx = dy = 0;
      sw = dw;
      sh = dh;
      if (-1 != h->enc) {
          sw = ei[h->enc].width;
          sh = ei[h->enc].height;
      }
      if (NULL == h->vi_gc)
          h->vi_gc = XCreateGC(dpy, win, 0, NULL);
      ng_ratio_fixup(&dw,&dh,&dx,&dy);
      if (0 == grabbed)
          if (Success == XvGrabPort(dpy,h->vi_port,CurrentTime))
            grabbed = 1;
      if (1 == grabbed) {
          XvPutVideo(dpy,h->vi_port,win,h->vi_gc,
                   sx,sy,sw,sh, dx,dy,dw,dh);
          if (debug)
            fprintf(stderr,"Xvideo: video: win=0x%lx, "
                  "src=%dx%d+%d+%d dst=%dx%d+%d+%d\n",
                  win, sw,sh,sx,sy, dw,dh,dx,dy);
      } else {
          fprintf(stderr,"Xvideo: port %ld busy\n",h->vi_port);
      }
    } else {
      if (grabbed) {
          XClearArea(dpy,win,0,0,0,0,False);
          XvStopVideo(dpy,h->vi_port,win);
          XvUngrabPort(dpy,h->vi_port,CurrentTime);
          grabbed = 0;
          if (debug)
            fprintf(stderr,"Xvideo: video off\n");
      }
    }
}

static int
xv_strlist_add(struct STRTAB **tab, char *str)
{
    int i;

    if (NULL == *tab) {
      *tab = malloc(sizeof(struct STRTAB)*2);
      i = 0;
    } else {
      for (i = 0; (*tab)[i].str != NULL; i++)
          if (0 == strcasecmp((*tab)[i].str,str))
            return (*tab)[i].nr;
      *tab = realloc(*tab,sizeof(struct STRTAB)*(i+2));
    }
    (*tab)[i].nr  = i;
    (*tab)[i].str = strdup(str);
    (*tab)[i+1].nr  = -1;
    (*tab)[i+1].str = NULL;
    return i;
}

static int xv_close(void *handle) { return 0; }

static int xv_flags(void *handle)
{
    struct xv_handle *h = handle;
    int ret = 0;

    ret |= CAN_OVERLAY;
    if (h->xv_freq != None)
      ret |= CAN_TUNE;
    return ret;
}

static struct ng_attribute* xv_attrs(void *handle)
{
    struct xv_handle *h  = handle;
    return h->attr;
}

/* ********************************************************************* */

void xv_video_init(unsigned int port, int hwscan)
{
    struct xv_handle *handle;
    struct STRTAB *norms  = NULL;
    struct STRTAB *inputs = NULL;
    char *h;
    int n, i, vi_port = -1, vi_adaptor = -1;

    if (Success != XvQueryExtension(dpy,&ver,&rel,&req,&ev,&err)) {
      if (debug)
          fprintf(stderr,"Xvideo: Server has no Xvideo extention support\n");
      return;
    }
    if (Success != XvQueryAdaptors(dpy,DefaultRootWindow(dpy),&adaptors,&ai)) {
      fprintf(stderr,"Xvideo: XvQueryAdaptors failed");
      exit(1);
    }
    if (debug)
      fprintf(stderr,"Xvideo: %d adaptors available.\n",adaptors);
    for (i = 0; i < adaptors; i++) {
      if (debug)
          fprintf(stderr,"Xvideo: %s:%s%s%s%s%s, ports %ld-%ld\n",
                ai[i].name,
                (ai[i].type & XvInputMask)  ? " input"  : "",
                (ai[i].type & XvOutputMask) ? " output" : "",
                (ai[i].type & XvVideoMask)  ? " video"  : "",
                (ai[i].type & XvStillMask)  ? " still"  : "",
                (ai[i].type & XvImageMask)  ? " image"  : "",
                ai[i].base_id,
                ai[i].base_id+ai[i].num_ports-1);
      if (hwscan) {
          /* just print some info's about the Xvideo port */
          n = fprintf(stderr,"port %ld-%ld",
                  ai[i].base_id,ai[i].base_id+ai[i].num_ports-1);
          if ((ai[i].type & XvInputMask) &&
            (ai[i].type & XvVideoMask))
            fprintf(stderr,"%*s[ -xvport %ld ]",40-n,"",ai[i].base_id);
          fprintf(stderr,"\n");
          if ((ai[i].type & XvInputMask) &&
            (ai[i].type & XvVideoMask))
            fprintf(stderr,"    type : Xvideo, video overlay\n");
          if ((ai[i].type & XvInputMask) &&
            (ai[i].type & XvImageMask))
            fprintf(stderr,"    type : Xvideo, image scaler\n");
          fprintf(stderr,"    name : %s\n",ai[i].name);
          fprintf(stderr,"\n");
          continue;
      }
      
      if ((ai[i].type & XvInputMask) &&
          (ai[i].type & XvVideoMask) &&
          (vi_port == -1)) {
          if (0 == port) {
            vi_port = ai[i].base_id;
            vi_adaptor = i;
          } else if (port >= ai[i].base_id  &&
                   port <  ai[i].base_id+ai[i].num_ports) {
            vi_port = port;
            vi_adaptor = i;
          } else {
            if (debug)
                fprintf(stderr,"Xvideo: skipping ports %ld-%ld (configured other: %d)\n",
                      ai[i].base_id, ai[i].base_id+ai[i].num_ports-1, port);
          }
      }
    }
    if (hwscan)
      return;

    /* *** video port *** */
    if (vi_port == -1) {
      if (debug)
          fprintf(stderr,"Xvideo: no usable video port found\n");
    } else {
      if (debug)
          fprintf(stderr,"Xvideo: using port %d for video\n",vi_port);
      handle = malloc(sizeof(struct xv_handle));
      memset(handle,0,sizeof(struct xv_handle));
      handle->vi_port     = vi_port;
      handle->vi_adaptor  = vi_adaptor;
      handle->xv_encoding = None;
      handle->xv_freq     = None;
      handle->xv_colorkey = None;
      handle->enc         = -1;
      handle->norm        = -1;
      handle->input       = -1;

      /* query encoding list */
      if (Success != XvQueryEncodings(dpy, vi_port,
                              &handle->encodings, &ei)) {
          fprintf(stderr,"Oops: XvQueryEncodings failed\n");
          exit(1);
      }
      handle->enc_map = malloc(sizeof(struct ENC_MAP)*handle->encodings);
      for (i = 0; i < handle->encodings; i++) {
          if (NULL != (h = strrchr(ei[i].name,'-'))) {
            *(h++) = 0;
            handle->enc_map[i].input = xv_strlist_add(&inputs,h);
          }
          handle->enc_map[i].norm = xv_strlist_add(&norms,ei[i].name);
          handle->enc_map[i].encoding = ei[i].encoding_id;
      }

      /* build atoms */
      at = XvQueryPortAttributes(dpy,vi_port,&attributes);
      for (i = 0; i < attributes; i++) {
          if (debug)
            fprintf(stderr,"  %s%s%s, %i -> %i\n",
                  at[i].name,
                  (at[i].flags & XvGettable) ? " get" : "",
                  (at[i].flags & XvSettable) ? " set" : "",
                  at[i].min_value,at[i].max_value);
            if (0 == strcmp("XV_ENCODING",at[i].name))
                handle->xv_encoding = XV_ENCODING;
            if (0 == strcmp("XV_FREQ",at[i].name))
                handle->xv_freq     = XV_FREQ;
#if 0
            if (0 == strcmp("XV_COLORKEY",at[i].name))
                handle->xv_colorkey = XV_COLORKEY;
#endif
          xv_add_attr(handle, 0, 0, 0, NULL, at+i);
      }

      if (handle->xv_encoding != None) {
          if (norms)
            xv_add_attr(handle, ATTR_ID_NORM, ATTR_TYPE_CHOICE,
                      0, norms, NULL);
          if (inputs)
            xv_add_attr(handle, ATTR_ID_INPUT, ATTR_TYPE_CHOICE,
                      0, inputs, NULL);
      }
#if 0
      if (xv_colorkey != None) {
          XvGetPortAttribute(dpy,vi_port,xv_colorkey,&xv.colorkey);
          fprintf(stderr,"Xvideo: colorkey: %x\n",xv.colorkey);
      }
#endif
      have_xv = 1;
      drv   = &xv_driver;
      h_drv = handle;
      f_drv = xv_flags(h_drv);
      add_attrs(xv_attrs(h_drv));
    }
}

/* ********************************************************************* */

#if 0
static Window icon_win;
static int icon_width,icon_height;

static void
icon_event(Widget widget, XtPointer client_data, XEvent *event, Boolean *d)
{
    switch (event->type) {
    case Expose:
      if (debug)
          fprintf(stderr,"icon expose\n");
      xv_video(icon_win, icon_width, icon_height, 1);
      break;
    case MapNotify:
      if (debug)
          fprintf(stderr,"icon map\n");
      xv_video(icon_win, icon_width, icon_height, 1);
      break;
    case UnmapNotify:
      if (debug)
          fprintf(stderr,"icon unmap\n");
      break;
    default:
      fprintf(stderr,"icon other\n");
      break;
    }
}

void
init_icon_window(Widget shell,WidgetClass class)
{
    Window root = RootWindowOfScreen(XtScreen(shell));
    Widget widget;
    XIconSize *is;
    int i,count;

    if (XGetIconSizes(XtDisplay(shell),root,&is,&count)) {
      for (i = 0; i < count; i++) {
          fprintf(stderr,"icon size: min=%dx%d - max=%dx%d - inc=%dx%d\n",
                is[i].min_width, is[i].min_height,
                is[i].max_width, is[i].max_height,
                is[i].width_inc, is[i].height_inc);
      }
      icon_width  = is[0].max_width;
      icon_height = is[0].max_height;
      if (icon_width * 3 > icon_height * 4) {
          while (icon_width * 3 > icon_height * 4 &&
               icon_width - is[0].width_inc > is[0].min_width)
            icon_width -= is[0].width_inc;
      } else {
          while (icon_width * 3 < icon_height * 4 &&
               icon_height - is[0].height_inc > is[0].min_height)
            icon_height -= is[0].height_inc;
      }
    } else {
      icon_width  = 64;
      icon_height = 48;
    }
    fprintf(stderr,"icon init %dx%d\n",icon_width,icon_height);
    
    icon_win = XCreateWindow(XtDisplay(shell),root,
                       0,0,icon_width,icon_height,1,
                       CopyFromParent,InputOutput,CopyFromParent,
                       0,NULL);
    widget = XtVaCreateWidget("icon",class,shell,NULL);
    XtRegisterDrawable(XtDisplay(shell),icon_win,widget);
    XtAddEventHandler(widget,StructureNotifyMask | ExposureMask,
                  False,icon_event,NULL);
    XSelectInput(XtDisplay(shell),icon_win,
             StructureNotifyMask | ExposureMask);
    XtVaSetValues(shell,XtNiconWindow,icon_win,NULL);
}
#endif

/* ********************************************************************* */

const struct ng_vid_driver xv_driver = {
    name:          "Xvideo",
    close:         xv_close,

    capabilities:  xv_flags,
    list_attrs:    xv_attrs,

    overlay:       xv_overlay,

    getfreq:       xv_getfreq,
    setfreq:       xv_setfreq,
    is_tuned:      xv_tuned,
};

#else /* HAVE_LIBXV */

int               have_xv = 0;

#endif /* HAVE_LIBXV */

Generated by  Doxygen 1.6.0   Back to index