• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

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 
00039 #include "config.h"
00040 #include "libavformat/avformat.h"
00041 #include "libavformat/internal.h"
00042 #include "libavutil/log.h"
00043 #include "libavutil/opt.h"
00044 #include "libavutil/parseutils.h"
00045 #include <time.h>
00046 #include <X11/X.h>
00047 #include <X11/Xlib.h>
00048 #include <X11/Xlibint.h>
00049 #include <X11/Xproto.h>
00050 #include <X11/Xutil.h>
00051 #include <sys/shm.h>
00052 #include <X11/extensions/shape.h>
00053 #include <X11/extensions/XShm.h>
00054 #include <X11/extensions/Xfixes.h>
00055 
00059 struct x11_grab
00060 {
00061     const AVClass *class;    
00062     int frame_size;          
00063     AVRational time_base;    
00064     int64_t time_frame;      
00066     char *video_size;        
00067     int height;              
00068     int width;               
00069     int x_off;               
00070     int y_off;               
00072     Display *dpy;            
00073     XImage *image;           
00074     int use_shm;             
00075     XShmSegmentInfo shminfo; 
00076     int  draw_mouse;         
00077     int  follow_mouse;       
00078     int  show_region;        
00079     char *framerate;         
00081     Window region_win;       
00082 };
00083 
00084 #define REGION_WIN_BORDER 3
00085 
00090 static void
00091 x11grab_draw_region_win(struct x11_grab *s)
00092 {
00093     Display *dpy = s->dpy;
00094     int screen;
00095     Window win = s->region_win;
00096     GC gc;
00097 
00098     screen = DefaultScreen(dpy);
00099     gc = XCreateGC(dpy, win, 0, 0);
00100     XSetForeground(dpy, gc, WhitePixel(dpy, screen));
00101     XSetBackground(dpy, gc, BlackPixel(dpy, screen));
00102     XSetLineAttributes(dpy, gc, REGION_WIN_BORDER, LineDoubleDash, 0, 0);
00103     XDrawRectangle(dpy, win, gc,
00104                    1, 1,
00105                    (s->width  + REGION_WIN_BORDER * 2) - 1 * 2 - 1,
00106                    (s->height + REGION_WIN_BORDER * 2) - 1 * 2 - 1);
00107     XFreeGC(dpy, gc);
00108 }
00109 
00115 static void
00116 x11grab_region_win_init(struct x11_grab *s)
00117 {
00118     Display *dpy = s->dpy;
00119     int screen;
00120     XSetWindowAttributes attribs;
00121     XRectangle rect;
00122 
00123     screen = DefaultScreen(dpy);
00124     attribs.override_redirect = True;
00125     s->region_win = XCreateWindow(dpy, RootWindow(dpy, screen),
00126                                   s->x_off  - REGION_WIN_BORDER,
00127                                   s->y_off  - REGION_WIN_BORDER,
00128                                   s->width  + REGION_WIN_BORDER * 2,
00129                                   s->height + REGION_WIN_BORDER * 2,
00130                                   0, CopyFromParent,
00131                                   InputOutput, CopyFromParent,
00132                                   CWOverrideRedirect, &attribs);
00133     rect.x = 0;
00134     rect.y = 0;
00135     rect.width  = s->width;
00136     rect.height = s->height;
00137     XShapeCombineRectangles(dpy, s->region_win,
00138                             ShapeBounding, REGION_WIN_BORDER, REGION_WIN_BORDER,
00139                             &rect, 1, ShapeSubtract, 0);
00140     XMapWindow(dpy, s->region_win);
00141     XSelectInput(dpy, s->region_win, ExposureMask | StructureNotifyMask);
00142     x11grab_draw_region_win(s);
00143 }
00144 
00156 static int
00157 x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
00158 {
00159     struct x11_grab *x11grab = s1->priv_data;
00160     Display *dpy;
00161     AVStream *st = NULL;
00162     enum PixelFormat input_pixfmt;
00163     XImage *image;
00164     int x_off = 0;
00165     int y_off = 0;
00166     int screen;
00167     int use_shm;
00168     char *param, *offset;
00169     int ret = 0;
00170     AVRational framerate;
00171 
00172     param = av_strdup(s1->filename);
00173     offset = strchr(param, '+');
00174     if (offset) {
00175         sscanf(offset, "%d,%d", &x_off, &y_off);
00176         x11grab->draw_mouse = !strstr(offset, "nomouse");
00177         *offset= 0;
00178     }
00179 
00180     if ((ret = av_parse_video_size(&x11grab->width, &x11grab->height, x11grab->video_size)) < 0) {
00181         av_log(s1, AV_LOG_ERROR, "Couldn't parse video size.\n");
00182         goto out;
00183     }
00184     if ((ret = av_parse_video_rate(&framerate, x11grab->framerate)) < 0) {
00185         av_log(s1, AV_LOG_ERROR, "Could not parse framerate: %s.\n", x11grab->framerate);
00186         goto out;
00187     }
00188     av_log(s1, AV_LOG_INFO, "device: %s -> display: %s x: %d y: %d width: %d height: %d\n",
00189            s1->filename, param, x_off, y_off, x11grab->width, x11grab->height);
00190 
00191     dpy = XOpenDisplay(param);
00192     if(!dpy) {
00193         av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
00194         ret = AVERROR(EIO);
00195         goto out;
00196     }
00197 
00198     st = avformat_new_stream(s1, NULL);
00199     if (!st) {
00200         ret = AVERROR(ENOMEM);
00201         goto out;
00202     }
00203     avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
00204 
00205     screen = DefaultScreen(dpy);
00206 
00207     if (x11grab->follow_mouse) {
00208         int screen_w, screen_h;
00209         Window w;
00210 
00211         screen_w = DisplayWidth(dpy, screen);
00212         screen_h = DisplayHeight(dpy, screen);
00213         XQueryPointer(dpy, RootWindow(dpy, screen), &w, &w, &x_off, &y_off, &ret, &ret, &ret);
00214         x_off -= x11grab->width / 2;
00215         y_off -= x11grab->height / 2;
00216         x_off = FFMIN(FFMAX(x_off, 0), screen_w - x11grab->width);
00217         y_off = FFMIN(FFMAX(y_off, 0), screen_h - x11grab->height);
00218         av_log(s1, AV_LOG_INFO, "followmouse is enabled, resetting grabbing region to x: %d y: %d\n", x_off, y_off);
00219     }
00220 
00221     use_shm = XShmQueryExtension(dpy);
00222     av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
00223 
00224     if(use_shm) {
00225         int scr = XDefaultScreen(dpy);
00226         image = XShmCreateImage(dpy,
00227                                 DefaultVisual(dpy, scr),
00228                                 DefaultDepth(dpy, scr),
00229                                 ZPixmap,
00230                                 NULL,
00231                                 &x11grab->shminfo,
00232                                 x11grab->width, x11grab->height);
00233         x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
00234                                         image->bytes_per_line * image->height,
00235                                         IPC_CREAT|0777);
00236         if (x11grab->shminfo.shmid == -1) {
00237             av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
00238             ret = AVERROR(ENOMEM);
00239             goto out;
00240         }
00241         x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
00242         x11grab->shminfo.readOnly = False;
00243 
00244         if (!XShmAttach(dpy, &x11grab->shminfo)) {
00245             av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
00246             /* needs some better error subroutine :) */
00247             ret = AVERROR(EIO);
00248             goto out;
00249         }
00250     } else {
00251         image = XGetImage(dpy, RootWindow(dpy, screen),
00252                           x_off,y_off,
00253                           x11grab->width, x11grab->height,
00254                           AllPlanes, ZPixmap);
00255     }
00256 
00257     switch (image->bits_per_pixel) {
00258     case 8:
00259         av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
00260         input_pixfmt = PIX_FMT_PAL8;
00261         break;
00262     case 16:
00263         if (       image->red_mask   == 0xf800 &&
00264                    image->green_mask == 0x07e0 &&
00265                    image->blue_mask  == 0x001f ) {
00266             av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
00267             input_pixfmt = PIX_FMT_RGB565;
00268         } else if (image->red_mask   == 0x7c00 &&
00269                    image->green_mask == 0x03e0 &&
00270                    image->blue_mask  == 0x001f ) {
00271             av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
00272             input_pixfmt = PIX_FMT_RGB555;
00273         } else {
00274             av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
00275             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);
00276             ret = AVERROR(EIO);
00277             goto out;
00278         }
00279         break;
00280     case 24:
00281         if (        image->red_mask   == 0xff0000 &&
00282                     image->green_mask == 0x00ff00 &&
00283                     image->blue_mask  == 0x0000ff ) {
00284             input_pixfmt = PIX_FMT_BGR24;
00285         } else if ( image->red_mask   == 0x0000ff &&
00286                     image->green_mask == 0x00ff00 &&
00287                     image->blue_mask  == 0xff0000 ) {
00288             input_pixfmt = PIX_FMT_RGB24;
00289         } else {
00290             av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
00291             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);
00292             ret = AVERROR(EIO);
00293             goto out;
00294         }
00295         break;
00296     case 32:
00297         input_pixfmt = PIX_FMT_RGB32;
00298         break;
00299     default:
00300         av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
00301         ret = AVERROR(EINVAL);
00302         goto out;
00303     }
00304 
00305     x11grab->frame_size = x11grab->width * x11grab->height * image->bits_per_pixel/8;
00306     x11grab->dpy = dpy;
00307     x11grab->time_base  = (AVRational){framerate.den, framerate.num};
00308     x11grab->time_frame = av_gettime() / av_q2d(x11grab->time_base);
00309     x11grab->x_off = x_off;
00310     x11grab->y_off = y_off;
00311     x11grab->image = image;
00312     x11grab->use_shm = use_shm;
00313 
00314     st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00315     st->codec->codec_id = CODEC_ID_RAWVIDEO;
00316     st->codec->width  = x11grab->width;
00317     st->codec->height = x11grab->height;
00318     st->codec->pix_fmt = input_pixfmt;
00319     st->codec->time_base = x11grab->time_base;
00320     st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(x11grab->time_base) * 8;
00321 
00322 out:
00323     return ret;
00324 }
00325 
00333 static void
00334 paint_mouse_pointer(XImage *image, struct x11_grab *s)
00335 {
00336     int x_off = s->x_off;
00337     int y_off = s->y_off;
00338     int width = s->width;
00339     int height = s->height;
00340     Display *dpy = s->dpy;
00341     XFixesCursorImage *xcim;
00342     int x, y;
00343     int line, column;
00344     int to_line, to_column;
00345     int pixstride = image->bits_per_pixel >> 3;
00346     /* Warning: in its insanity, xlib provides unsigned image data through a
00347      * char* pointer, so we have to make it uint8_t to make things not break.
00348      * Anyone who performs further investigation of the xlib API likely risks
00349      * permanent brain damage. */
00350     uint8_t *pix = image->data;
00351 
00352     /* Code doesn't currently support 16-bit or PAL8 */
00353     if (image->bits_per_pixel != 24 && image->bits_per_pixel != 32)
00354         return;
00355 
00356     xcim = XFixesGetCursorImage(dpy);
00357 
00358     x = xcim->x - xcim->xhot;
00359     y = xcim->y - xcim->yhot;
00360 
00361     to_line = FFMIN((y + xcim->height), (height + y_off));
00362     to_column = FFMIN((x + xcim->width), (width + x_off));
00363 
00364     for (line = FFMAX(y, y_off); line < to_line; line++) {
00365         for (column = FFMAX(x, x_off); column < to_column; column++) {
00366             int  xcim_addr = (line - y) * xcim->width + column - x;
00367             int image_addr = ((line - y_off) * width + column - x_off) * pixstride;
00368             int r = (uint8_t)(xcim->pixels[xcim_addr] >>  0);
00369             int g = (uint8_t)(xcim->pixels[xcim_addr] >>  8);
00370             int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16);
00371             int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24);
00372 
00373             if (a == 255) {
00374                 pix[image_addr+0] = r;
00375                 pix[image_addr+1] = g;
00376                 pix[image_addr+2] = b;
00377             } else if (a) {
00378                 /* pixel values from XFixesGetCursorImage come premultiplied by alpha */
00379                 pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255;
00380                 pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255;
00381                 pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255;
00382             }
00383         }
00384     }
00385 
00386     XFree(xcim);
00387     xcim = NULL;
00388 }
00389 
00390 
00401 static int
00402 xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
00403 {
00404     xGetImageReply rep;
00405     xGetImageReq *req;
00406     long nbytes;
00407 
00408     if (!image) {
00409         return 0;
00410     }
00411 
00412     LockDisplay(dpy);
00413     GetReq(GetImage, req);
00414 
00415     /* First set up the standard stuff in the request */
00416     req->drawable = d;
00417     req->x = x;
00418     req->y = y;
00419     req->width = image->width;
00420     req->height = image->height;
00421     req->planeMask = (unsigned int)AllPlanes;
00422     req->format = ZPixmap;
00423 
00424     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
00425         UnlockDisplay(dpy);
00426         SyncHandle();
00427         return 0;
00428     }
00429 
00430     nbytes = (long)rep.length << 2;
00431     _XReadPad(dpy, image->data, nbytes);
00432 
00433     UnlockDisplay(dpy);
00434     SyncHandle();
00435     return 1;
00436 }
00437 
00445 static int
00446 x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
00447 {
00448     struct x11_grab *s = s1->priv_data;
00449     Display *dpy = s->dpy;
00450     XImage *image = s->image;
00451     int x_off = s->x_off;
00452     int y_off = s->y_off;
00453 
00454     int screen;
00455     Window root;
00456     int follow_mouse = s->follow_mouse;
00457 
00458     int64_t curtime, delay;
00459     struct timespec ts;
00460 
00461     /* Calculate the time of the next frame */
00462     s->time_frame += INT64_C(1000000);
00463 
00464     /* wait based on the frame rate */
00465     for(;;) {
00466         curtime = av_gettime();
00467         delay = s->time_frame * av_q2d(s->time_base) - curtime;
00468         if (delay <= 0) {
00469             if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
00470                 s->time_frame += INT64_C(1000000);
00471             }
00472             break;
00473         }
00474         ts.tv_sec = delay / 1000000;
00475         ts.tv_nsec = (delay % 1000000) * 1000;
00476         nanosleep(&ts, NULL);
00477     }
00478 
00479     av_init_packet(pkt);
00480     pkt->data = image->data;
00481     pkt->size = s->frame_size;
00482     pkt->pts = curtime;
00483 
00484     screen = DefaultScreen(dpy);
00485     root = RootWindow(dpy, screen);
00486     if (follow_mouse) {
00487         int screen_w, screen_h;
00488         int pointer_x, pointer_y, _;
00489         Window w;
00490 
00491         screen_w = DisplayWidth(dpy, screen);
00492         screen_h = DisplayHeight(dpy, screen);
00493         XQueryPointer(dpy, root, &w, &w, &pointer_x, &pointer_y, &_, &_, &_);
00494         if (follow_mouse == -1) {
00495             // follow the mouse, put it at center of grabbing region
00496             x_off += pointer_x - s->width  / 2 - x_off;
00497             y_off += pointer_y - s->height / 2 - y_off;
00498         } else {
00499             // follow the mouse, but only move the grabbing region when mouse
00500             // reaches within certain pixels to the edge.
00501             if (pointer_x > x_off + s->width - follow_mouse) {
00502                 x_off += pointer_x - (x_off + s->width - follow_mouse);
00503             } else if (pointer_x < x_off + follow_mouse)
00504                 x_off -= (x_off + follow_mouse) - pointer_x;
00505             if (pointer_y > y_off + s->height - follow_mouse) {
00506                 y_off += pointer_y - (y_off + s->height - follow_mouse);
00507             } else if (pointer_y < y_off + follow_mouse)
00508                 y_off -= (y_off + follow_mouse) - pointer_y;
00509         }
00510         // adjust grabbing region position if it goes out of screen.
00511         s->x_off = x_off = FFMIN(FFMAX(x_off, 0), screen_w - s->width);
00512         s->y_off = y_off = FFMIN(FFMAX(y_off, 0), screen_h - s->height);
00513 
00514         if (s->show_region && s->region_win)
00515             XMoveWindow(dpy, s->region_win,
00516                         s->x_off - REGION_WIN_BORDER,
00517                         s->y_off - REGION_WIN_BORDER);
00518     }
00519 
00520     if (s->show_region) {
00521         if (s->region_win) {
00522             XEvent evt;
00523             // clean up the events, and do the initinal draw or redraw.
00524             for (evt.type = NoEventMask; XCheckMaskEvent(dpy, ExposureMask | StructureNotifyMask, &evt); );
00525             if (evt.type)
00526                 x11grab_draw_region_win(s);
00527         } else {
00528             x11grab_region_win_init(s);
00529         }
00530     }
00531 
00532     if(s->use_shm) {
00533         if (!XShmGetImage(dpy, root, image, x_off, y_off, AllPlanes)) {
00534             av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
00535         }
00536     } else {
00537         if (!xget_zpixmap(dpy, root, image, x_off, y_off)) {
00538             av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
00539         }
00540     }
00541 
00542     if (s->draw_mouse) {
00543         paint_mouse_pointer(image, s);
00544     }
00545 
00546     return s->frame_size;
00547 }
00548 
00555 static int
00556 x11grab_read_close(AVFormatContext *s1)
00557 {
00558     struct x11_grab *x11grab = s1->priv_data;
00559 
00560     /* Detach cleanly from shared mem */
00561     if (x11grab->use_shm) {
00562         XShmDetach(x11grab->dpy, &x11grab->shminfo);
00563         shmdt(x11grab->shminfo.shmaddr);
00564         shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL);
00565     }
00566 
00567     /* Destroy X11 image */
00568     if (x11grab->image) {
00569         XDestroyImage(x11grab->image);
00570         x11grab->image = NULL;
00571     }
00572 
00573     if (x11grab->region_win) {
00574         XDestroyWindow(x11grab->dpy, x11grab->region_win);
00575     }
00576 
00577     /* Free X11 display */
00578     XCloseDisplay(x11grab->dpy);
00579     return 0;
00580 }
00581 
00582 #define OFFSET(x) offsetof(struct x11_grab, x)
00583 #define DEC AV_OPT_FLAG_DECODING_PARAM
00584 static const AVOption options[] = {
00585     { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = "vga"}, 0, 0, DEC },
00586     { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "ntsc"}, 0, 0, DEC },
00587     { "draw_mouse", "Draw the mouse pointer.", OFFSET(draw_mouse), AV_OPT_TYPE_INT, { 1 }, 0, 1, DEC },
00588     { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
00589       OFFSET(follow_mouse), AV_OPT_TYPE_INT, { 0 }, -1, INT_MAX, DEC, "follow_mouse" },
00590     { "centered", "Keep the mouse pointer at the center of grabbing region when following.", 0, AV_OPT_TYPE_CONST, { -1 }, INT_MIN, INT_MAX, DEC, "follow_mouse" },
00591     { "show_region", "Show the grabbing region.", OFFSET(show_region), AV_OPT_TYPE_INT, { 0 }, 0, 1, DEC },
00592     { NULL },
00593 };
00594 
00595 static const AVClass x11_class = {
00596     .class_name = "X11grab indev",
00597     .item_name  = av_default_item_name,
00598     .option     = options,
00599     .version    = LIBAVUTIL_VERSION_INT,
00600 };
00601 
00603 AVInputFormat ff_x11_grab_device_demuxer = {
00604     .name           = "x11grab",
00605     .long_name      = NULL_IF_CONFIG_SMALL("X11grab"),
00606     .priv_data_size = sizeof(struct x11_grab),
00607     .read_header    = x11grab_read_header,
00608     .read_packet    = x11grab_read_packet,
00609     .read_close     = x11grab_read_close,
00610     .flags          = AVFMT_NOFILE,
00611     .priv_class     = &x11_class,
00612 };
Generated on Thu Jul 11 2013 15:38:23 for Libav by doxygen 1.7.1