Libav 0.7.1
libavdevice/x11grab.c
Go to the documentation of this file.
00001 /*
00002  * X11 video grab interface
00003  *
00004  * This file is part of Libav.
00005  *
00006  * Libav integration:
00007  * Copyright (C) 2006 Clemens Fruhwirth <clemens@endorphin.org>
00008  *                    Edouard Gomez <ed.gomez@free.fr>
00009  *
00010  * This file contains code from grab.c:
00011  * Copyright (c) 2000-2001 Fabrice Bellard
00012  *
00013  * This file contains code from the xvidcap project:
00014  * Copyright (C) 1997-1998 Rasca, Berlin
00015  *               2003-2004 Karl H. Beckers, Frankfurt
00016  *
00017  * Libav is free software; you can redistribute it and/or modify
00018  * it under the terms of the GNU General Public License as published by
00019  * the Free Software Foundation; either version 2 of the License, or
00020  * (at your option) any later version.
00021  *
00022  * Libav is distributed in the hope that it will be useful,
00023  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00024  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00025  * GNU General Public License for more details.
00026  *
00027  * You should have received a copy of the GNU General Public License
00028  * along with Libav; if not, write to the Free Software
00029  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00030  */
00031 
00038 #include "config.h"
00039 #include "libavformat/avformat.h"
00040 #include "libavutil/log.h"
00041 #include "libavutil/opt.h"
00042 #include "libavutil/parseutils.h"
00043 #include <time.h>
00044 #include <X11/X.h>
00045 #include <X11/Xlib.h>
00046 #include <X11/Xlibint.h>
00047 #include <X11/Xproto.h>
00048 #include <X11/Xutil.h>
00049 #include <sys/shm.h>
00050 #include <X11/extensions/XShm.h>
00051 #include <X11/extensions/Xfixes.h>
00052 
00056 struct x11_grab
00057 {
00058     const AVClass *class;    
00059     int frame_size;          
00060     AVRational time_base;    
00061     int64_t time_frame;      
00063     char *video_size;        
00064     int height;              
00065     int width;               
00066     int x_off;               
00067     int y_off;               
00069     Display *dpy;            
00070     XImage *image;           
00071     int use_shm;             
00072     XShmSegmentInfo shminfo; 
00073     int nomouse;
00074     char *framerate;         
00075 };
00076 
00088 static int
00089 x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
00090 {
00091     struct x11_grab *x11grab = s1->priv_data;
00092     Display *dpy;
00093     AVStream *st = NULL;
00094     enum PixelFormat input_pixfmt;
00095     XImage *image;
00096     int x_off = 0;
00097     int y_off = 0;
00098     int use_shm;
00099     char *param, *offset;
00100     int ret = 0;
00101     AVRational framerate;
00102 
00103     param = av_strdup(s1->filename);
00104     offset = strchr(param, '+');
00105     if (offset) {
00106         sscanf(offset, "%d,%d", &x_off, &y_off);
00107         x11grab->nomouse= strstr(offset, "nomouse");
00108         *offset= 0;
00109     }
00110 
00111     if ((ret = av_parse_video_size(&x11grab->width, &x11grab->height, x11grab->video_size)) < 0) {
00112         av_log(s1, AV_LOG_ERROR, "Couldn't parse video size.\n");
00113         goto out;
00114     }
00115     if ((ret = av_parse_video_rate(&framerate, x11grab->framerate)) < 0) {
00116         av_log(s1, AV_LOG_ERROR, "Could not parse framerate: %s.\n", x11grab->framerate);
00117         goto out;
00118     }
00119 #if FF_API_FORMAT_PARAMETERS
00120     if (ap->width > 0)
00121         x11grab->width = ap->width;
00122     if (ap->height > 0)
00123         x11grab->height = ap->height;
00124     if (ap->time_base.num)
00125         framerate = (AVRational){ap->time_base.den, ap->time_base.num};
00126 #endif
00127     av_log(s1, AV_LOG_INFO, "device: %s -> display: %s x: %d y: %d width: %d height: %d\n",
00128            s1->filename, param, x_off, y_off, x11grab->width, x11grab->height);
00129 
00130     dpy = XOpenDisplay(param);
00131     if(!dpy) {
00132         av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
00133         ret = AVERROR(EIO);
00134         goto out;
00135     }
00136 
00137     st = av_new_stream(s1, 0);
00138     if (!st) {
00139         ret = AVERROR(ENOMEM);
00140         goto out;
00141     }
00142     av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
00143 
00144     use_shm = XShmQueryExtension(dpy);
00145     av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
00146 
00147     if(use_shm) {
00148         int scr = XDefaultScreen(dpy);
00149         image = XShmCreateImage(dpy,
00150                                 DefaultVisual(dpy, scr),
00151                                 DefaultDepth(dpy, scr),
00152                                 ZPixmap,
00153                                 NULL,
00154                                 &x11grab->shminfo,
00155                                 x11grab->width, x11grab->height);
00156         x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
00157                                         image->bytes_per_line * image->height,
00158                                         IPC_CREAT|0777);
00159         if (x11grab->shminfo.shmid == -1) {
00160             av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
00161             ret = AVERROR(ENOMEM);
00162             goto out;
00163         }
00164         x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
00165         x11grab->shminfo.readOnly = False;
00166 
00167         if (!XShmAttach(dpy, &x11grab->shminfo)) {
00168             av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
00169             /* needs some better error subroutine :) */
00170             ret = AVERROR(EIO);
00171             goto out;
00172         }
00173     } else {
00174         image = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)),
00175                           x_off,y_off,
00176                           x11grab->width, x11grab->height,
00177                           AllPlanes, ZPixmap);
00178     }
00179 
00180     switch (image->bits_per_pixel) {
00181     case 8:
00182         av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
00183         input_pixfmt = PIX_FMT_PAL8;
00184         break;
00185     case 16:
00186         if (       image->red_mask   == 0xf800 &&
00187                    image->green_mask == 0x07e0 &&
00188                    image->blue_mask  == 0x001f ) {
00189             av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
00190             input_pixfmt = PIX_FMT_RGB565;
00191         } else if (image->red_mask   == 0x7c00 &&
00192                    image->green_mask == 0x03e0 &&
00193                    image->blue_mask  == 0x001f ) {
00194             av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
00195             input_pixfmt = PIX_FMT_RGB555;
00196         } else {
00197             av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
00198             av_log(s1, AV_LOG_ERROR, "color masks: r 0x%.6lx g 0x%.6lx b 0x%.6lx\n", image->red_mask, image->green_mask, image->blue_mask);
00199             ret = AVERROR(EIO);
00200             goto out;
00201         }
00202         break;
00203     case 24:
00204         if (        image->red_mask   == 0xff0000 &&
00205                     image->green_mask == 0x00ff00 &&
00206                     image->blue_mask  == 0x0000ff ) {
00207             input_pixfmt = PIX_FMT_BGR24;
00208         } else if ( image->red_mask   == 0x0000ff &&
00209                     image->green_mask == 0x00ff00 &&
00210                     image->blue_mask  == 0xff0000 ) {
00211             input_pixfmt = PIX_FMT_RGB24;
00212         } else {
00213             av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
00214             av_log(s1, AV_LOG_ERROR, "color masks: r 0x%.6lx g 0x%.6lx b 0x%.6lx\n", image->red_mask, image->green_mask, image->blue_mask);
00215             ret = AVERROR(EIO);
00216             goto out;
00217         }
00218         break;
00219     case 32:
00220 #if 0
00221         GetColorInfo (image, &c_info);
00222         if ( c_info.alpha_mask == 0xff000000 && image->green_mask == 0x0000ff00) {
00223             /* byte order is relevant here, not endianness
00224              * endianness is handled by avcodec, but atm no such thing
00225              * as having ABGR, instead of ARGB in a word. Since we
00226              * need this for Solaris/SPARC, but need to do the conversion
00227              * for every frame we do it outside of this loop, cf. below
00228              * this matches both ARGB32 and ABGR32 */
00229             input_pixfmt = PIX_FMT_ARGB32;
00230         }  else {
00231             av_log(s1, AV_LOG_ERROR,"image depth %i not supported ... aborting\n", image->bits_per_pixel);
00232             return AVERROR(EIO);
00233         }
00234 #endif
00235         input_pixfmt = PIX_FMT_RGB32;
00236         break;
00237     default:
00238         av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
00239         ret = AVERROR(EINVAL);
00240         goto out;
00241     }
00242 
00243     x11grab->frame_size = x11grab->width * x11grab->height * image->bits_per_pixel/8;
00244     x11grab->dpy = dpy;
00245     x11grab->time_base  = (AVRational){framerate.den, framerate.num};
00246     x11grab->time_frame = av_gettime() / av_q2d(x11grab->time_base);
00247     x11grab->x_off = x_off;
00248     x11grab->y_off = y_off;
00249     x11grab->image = image;
00250     x11grab->use_shm = use_shm;
00251 
00252     st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00253     st->codec->codec_id = CODEC_ID_RAWVIDEO;
00254     st->codec->width  = x11grab->width;
00255     st->codec->height = x11grab->height;
00256     st->codec->pix_fmt = input_pixfmt;
00257     st->codec->time_base = x11grab->time_base;
00258     st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(x11grab->time_base) * 8;
00259 
00260 out:
00261     return ret;
00262 }
00263 
00271 static void
00272 paint_mouse_pointer(XImage *image, struct x11_grab *s)
00273 {
00274     int x_off = s->x_off;
00275     int y_off = s->y_off;
00276     int width = s->width;
00277     int height = s->height;
00278     Display *dpy = s->dpy;
00279     XFixesCursorImage *xcim;
00280     int x, y;
00281     int line, column;
00282     int to_line, to_column;
00283     int pixstride = image->bits_per_pixel >> 3;
00284     /* Warning: in its insanity, xlib provides unsigned image data through a
00285      * char* pointer, so we have to make it uint8_t to make things not break.
00286      * Anyone who performs further investigation of the xlib API likely risks
00287      * permanent brain damage. */
00288     uint8_t *pix = image->data;
00289 
00290     /* Code doesn't currently support 16-bit or PAL8 */
00291     if (image->bits_per_pixel != 24 && image->bits_per_pixel != 32)
00292         return;
00293 
00294     xcim = XFixesGetCursorImage(dpy);
00295 
00296     x = xcim->x - xcim->xhot;
00297     y = xcim->y - xcim->yhot;
00298 
00299     to_line = FFMIN((y + xcim->height), (height + y_off));
00300     to_column = FFMIN((x + xcim->width), (width + x_off));
00301 
00302     for (line = FFMAX(y, y_off); line < to_line; line++) {
00303         for (column = FFMAX(x, x_off); column < to_column; column++) {
00304             int  xcim_addr = (line - y) * xcim->width + column - x;
00305             int image_addr = ((line - y_off) * width + column - x_off) * pixstride;
00306             int r = (uint8_t)(xcim->pixels[xcim_addr] >>  0);
00307             int g = (uint8_t)(xcim->pixels[xcim_addr] >>  8);
00308             int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16);
00309             int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24);
00310 
00311             if (a == 255) {
00312                 pix[image_addr+0] = r;
00313                 pix[image_addr+1] = g;
00314                 pix[image_addr+2] = b;
00315             } else if (a) {
00316                 /* pixel values from XFixesGetCursorImage come premultiplied by alpha */
00317                 pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255;
00318                 pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255;
00319                 pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255;
00320             }
00321         }
00322     }
00323 
00324     XFree(xcim);
00325     xcim = NULL;
00326 }
00327 
00328 
00339 static int
00340 xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
00341 {
00342     xGetImageReply rep;
00343     xGetImageReq *req;
00344     long nbytes;
00345 
00346     if (!image) {
00347         return 0;
00348     }
00349 
00350     LockDisplay(dpy);
00351     GetReq(GetImage, req);
00352 
00353     /* First set up the standard stuff in the request */
00354     req->drawable = d;
00355     req->x = x;
00356     req->y = y;
00357     req->width = image->width;
00358     req->height = image->height;
00359     req->planeMask = (unsigned int)AllPlanes;
00360     req->format = ZPixmap;
00361 
00362     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
00363         UnlockDisplay(dpy);
00364         SyncHandle();
00365         return 0;
00366     }
00367 
00368     nbytes = (long)rep.length << 2;
00369     _XReadPad(dpy, image->data, nbytes);
00370 
00371     UnlockDisplay(dpy);
00372     SyncHandle();
00373     return 1;
00374 }
00375 
00383 static int
00384 x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
00385 {
00386     struct x11_grab *s = s1->priv_data;
00387     Display *dpy = s->dpy;
00388     XImage *image = s->image;
00389     int x_off = s->x_off;
00390     int y_off = s->y_off;
00391 
00392     int64_t curtime, delay;
00393     struct timespec ts;
00394 
00395     /* Calculate the time of the next frame */
00396     s->time_frame += INT64_C(1000000);
00397 
00398     /* wait based on the frame rate */
00399     for(;;) {
00400         curtime = av_gettime();
00401         delay = s->time_frame * av_q2d(s->time_base) - curtime;
00402         if (delay <= 0) {
00403             if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
00404                 s->time_frame += INT64_C(1000000);
00405             }
00406             break;
00407         }
00408         ts.tv_sec = delay / 1000000;
00409         ts.tv_nsec = (delay % 1000000) * 1000;
00410         nanosleep(&ts, NULL);
00411     }
00412 
00413     av_init_packet(pkt);
00414     pkt->data = image->data;
00415     pkt->size = s->frame_size;
00416     pkt->pts = curtime;
00417 
00418     if(s->use_shm) {
00419         if (!XShmGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off, AllPlanes)) {
00420             av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
00421         }
00422     } else {
00423         if (!xget_zpixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off)) {
00424             av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
00425         }
00426     }
00427 
00428     if(!s->nomouse){
00429         paint_mouse_pointer(image, s);
00430     }
00431 
00432     return s->frame_size;
00433 }
00434 
00441 static int
00442 x11grab_read_close(AVFormatContext *s1)
00443 {
00444     struct x11_grab *x11grab = s1->priv_data;
00445 
00446     /* Detach cleanly from shared mem */
00447     if (x11grab->use_shm) {
00448         XShmDetach(x11grab->dpy, &x11grab->shminfo);
00449         shmdt(x11grab->shminfo.shmaddr);
00450         shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL);
00451     }
00452 
00453     /* Destroy X11 image */
00454     if (x11grab->image) {
00455         XDestroyImage(x11grab->image);
00456         x11grab->image = NULL;
00457     }
00458 
00459     /* Free X11 display */
00460     XCloseDisplay(x11grab->dpy);
00461     return 0;
00462 }
00463 
00464 #define OFFSET(x) offsetof(struct x11_grab, x)
00465 #define DEC AV_OPT_FLAG_DECODING_PARAM
00466 static const AVOption options[] = {
00467     { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), FF_OPT_TYPE_STRING, {.str = "vga"}, 0, 0, DEC },
00468     { "framerate", "", OFFSET(framerate), FF_OPT_TYPE_STRING, {.str = "ntsc"}, 0, 0, DEC },
00469     { NULL },
00470 };
00471 
00472 static const AVClass x11_class = {
00473     .class_name = "X11grab indev",
00474     .item_name  = av_default_item_name,
00475     .option     = options,
00476     .version    = LIBAVUTIL_VERSION_INT,
00477 };
00478 
00480 AVInputFormat ff_x11_grab_device_demuxer =
00481 {
00482     "x11grab",
00483     NULL_IF_CONFIG_SMALL("X11grab"),
00484     sizeof(struct x11_grab),
00485     NULL,
00486     x11grab_read_header,
00487     x11grab_read_packet,
00488     x11grab_read_close,
00489     .flags = AVFMT_NOFILE,
00490     .priv_class = &x11_class,
00491 };