Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * X11 video grab interface
  3.  *
  4.  * This file is part of FFmpeg.
  5.  *
  6.  * FFmpeg integration:
  7.  * Copyright (C) 2006 Clemens Fruhwirth <clemens@endorphin.org>
  8.  *                    Edouard Gomez <ed.gomez@free.fr>
  9.  *
  10.  * This file contains code from grab.c:
  11.  * Copyright (c) 2000-2001 Fabrice Bellard
  12.  *
  13.  * This file contains code from the xvidcap project:
  14.  * Copyright (C) 1997-1998 Rasca, Berlin
  15.  *               2003-2004 Karl H. Beckers, Frankfurt
  16.  *
  17.  * FFmpeg is free software; you can redistribute it and/or modify
  18.  * it under the terms of the GNU General Public License as published by
  19.  * the Free Software Foundation; either version 2 of the License, or
  20.  * (at your option) any later version.
  21.  *
  22.  * FFmpeg is distributed in the hope that it will be useful,
  23.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  24.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  25.  * GNU General Public License for more details.
  26.  *
  27.  * You should have received a copy of the GNU General Public License
  28.  * along with FFmpeg; if not, write to the Free Software
  29.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  30.  */
  31.  
  32. /**
  33.  * @file
  34.  * X11 frame device demuxer
  35.  * @author Clemens Fruhwirth <clemens@endorphin.org>
  36.  * @author Edouard Gomez <ed.gomez@free.fr>
  37.  */
  38.  
  39. #include "config.h"
  40. #include "libavformat/internal.h"
  41. #include "libavutil/log.h"
  42. #include "libavutil/opt.h"
  43. #include "libavutil/parseutils.h"
  44. #include "libavutil/time.h"
  45. #include <time.h>
  46. #include <X11/cursorfont.h>
  47. #include <X11/X.h>
  48. #include <X11/Xlib.h>
  49. #include <X11/Xlibint.h>
  50. #include <X11/Xproto.h>
  51. #include <X11/Xutil.h>
  52. #include <sys/shm.h>
  53. #include <X11/extensions/shape.h>
  54. #include <X11/extensions/XShm.h>
  55. #include <X11/extensions/Xfixes.h>
  56. #include "avdevice.h"
  57.  
  58. /**
  59.  * X11 Device Demuxer context
  60.  */
  61. struct x11grab {
  62.     const AVClass *class;    /**< Class for private options. */
  63.     int frame_size;          /**< Size in bytes of a grabbed frame */
  64.     AVRational time_base;    /**< Time base */
  65.     int64_t time_frame;      /**< Current time */
  66.  
  67.     int width;               /**< Width of the grab frame */
  68.     int height;              /**< Height of the grab frame */
  69.     int x_off;               /**< Horizontal top-left corner coordinate */
  70.     int y_off;               /**< Vertical top-left corner coordinate */
  71.  
  72.     Display *dpy;            /**< X11 display from which x11grab grabs frames */
  73.     XImage *image;           /**< X11 image holding the grab */
  74.     int use_shm;             /**< !0 when using XShm extension */
  75.     XShmSegmentInfo shminfo; /**< When using XShm, keeps track of XShm infos */
  76.     int  draw_mouse;         /**< Set by a private option. */
  77.     int  follow_mouse;       /**< Set by a private option. */
  78.     int  show_region;        /**< set by a private option. */
  79.     AVRational framerate;         /**< Set by a private option. */
  80.     int palette_changed;
  81.     uint32_t palette[256];
  82.  
  83.     Cursor c;
  84.     Window region_win;       /**< This is used by show_region option. */
  85. };
  86.  
  87. #define REGION_WIN_BORDER 3
  88. /**
  89.  * Draw grabbing region window
  90.  *
  91.  * @param s x11grab context
  92.  */
  93. static void
  94. x11grab_draw_region_win(struct x11grab *s)
  95. {
  96.     Display *dpy = s->dpy;
  97.     int screen;
  98.     Window win = s->region_win;
  99.     GC gc;
  100.  
  101.     screen = DefaultScreen(dpy);
  102.     gc = XCreateGC(dpy, win, 0, 0);
  103.     XSetForeground(dpy, gc, WhitePixel(dpy, screen));
  104.     XSetBackground(dpy, gc, BlackPixel(dpy, screen));
  105.     XSetLineAttributes(dpy, gc, REGION_WIN_BORDER, LineDoubleDash, 0, 0);
  106.     XDrawRectangle(dpy, win, gc,
  107.                    1, 1,
  108.                    (s->width  + REGION_WIN_BORDER * 2) - 1 * 2 - 1,
  109.                    (s->height + REGION_WIN_BORDER * 2) - 1 * 2 - 1);
  110.     XFreeGC(dpy, gc);
  111. }
  112.  
  113. /**
  114.  * Initialize grabbing region window
  115.  *
  116.  * @param s x11grab context
  117.  */
  118. static void
  119. x11grab_region_win_init(struct x11grab *s)
  120. {
  121.     Display *dpy = s->dpy;
  122.     int screen;
  123.     XSetWindowAttributes attribs;
  124.     XRectangle rect;
  125.  
  126.     screen = DefaultScreen(dpy);
  127.     attribs.override_redirect = True;
  128.     s->region_win = XCreateWindow(dpy, RootWindow(dpy, screen),
  129.                                   s->x_off  - REGION_WIN_BORDER,
  130.                                   s->y_off  - REGION_WIN_BORDER,
  131.                                   s->width  + REGION_WIN_BORDER * 2,
  132.                                   s->height + REGION_WIN_BORDER * 2,
  133.                                   0, CopyFromParent,
  134.                                   InputOutput, CopyFromParent,
  135.                                   CWOverrideRedirect, &attribs);
  136.     rect.x = 0;
  137.     rect.y = 0;
  138.     rect.width  = s->width;
  139.     rect.height = s->height;
  140.     XShapeCombineRectangles(dpy, s->region_win,
  141.                             ShapeBounding, REGION_WIN_BORDER, REGION_WIN_BORDER,
  142.                             &rect, 1, ShapeSubtract, 0);
  143.     XMapWindow(dpy, s->region_win);
  144.     XSelectInput(dpy, s->region_win, ExposureMask | StructureNotifyMask);
  145.     x11grab_draw_region_win(s);
  146. }
  147.  
  148. /**
  149.  * Initialize the x11 grab device demuxer (public device demuxer API).
  150.  *
  151.  * @param s1 Context from avformat core
  152.  * @return <ul>
  153.  *          <li>AVERROR(ENOMEM) no memory left</li>
  154.  *          <li>AVERROR(EIO) other failure case</li>
  155.  *          <li>0 success</li>
  156.  *         </ul>
  157.  */
  158. static int
  159. x11grab_read_header(AVFormatContext *s1)
  160. {
  161.     struct x11grab *x11grab = s1->priv_data;
  162.     Display *dpy;
  163.     AVStream *st = NULL;
  164.     enum AVPixelFormat input_pixfmt;
  165.     XImage *image;
  166.     int x_off = 0;
  167.     int y_off = 0;
  168.     int screen;
  169.     int use_shm;
  170.     char *dpyname, *offset;
  171.     int ret = 0;
  172.     Colormap color_map;
  173.     XColor color[256];
  174.     int i;
  175.  
  176.     dpyname = av_strdup(s1->filename);
  177.     if (!dpyname)
  178.         goto out;
  179.  
  180.     offset = strchr(dpyname, '+');
  181.     if (offset) {
  182.         sscanf(offset, "%d,%d", &x_off, &y_off);
  183.         if (strstr(offset, "nomouse")) {
  184.             av_log(s1, AV_LOG_WARNING,
  185.                    "'nomouse' specification in argument is deprecated: "
  186.                    "use 'draw_mouse' option with value 0 instead\n");
  187.             x11grab->draw_mouse = 0;
  188.         }
  189.         *offset= 0;
  190.     }
  191.  
  192.     av_log(s1, AV_LOG_INFO, "device: %s -> display: %s x: %d y: %d width: %d height: %d\n",
  193.            s1->filename, dpyname, x_off, y_off, x11grab->width, x11grab->height);
  194.  
  195.     dpy = XOpenDisplay(dpyname);
  196.     av_freep(&dpyname);
  197.     if(!dpy) {
  198.         av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
  199.         ret = AVERROR(EIO);
  200.         goto out;
  201.     }
  202.  
  203.     st = avformat_new_stream(s1, NULL);
  204.     if (!st) {
  205.         ret = AVERROR(ENOMEM);
  206.         goto out;
  207.     }
  208.     avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
  209.  
  210.     screen = DefaultScreen(dpy);
  211.  
  212.     if (x11grab->follow_mouse) {
  213.         int screen_w, screen_h;
  214.         Window w;
  215.  
  216.         screen_w = DisplayWidth(dpy, screen);
  217.         screen_h = DisplayHeight(dpy, screen);
  218.         XQueryPointer(dpy, RootWindow(dpy, screen), &w, &w, &x_off, &y_off, &ret, &ret, &ret);
  219.         x_off -= x11grab->width / 2;
  220.         y_off -= x11grab->height / 2;
  221.         x_off = FFMIN(FFMAX(x_off, 0), screen_w - x11grab->width);
  222.         y_off = FFMIN(FFMAX(y_off, 0), screen_h - x11grab->height);
  223.         av_log(s1, AV_LOG_INFO, "followmouse is enabled, resetting grabbing region to x: %d y: %d\n", x_off, y_off);
  224.     }
  225.  
  226.     use_shm = XShmQueryExtension(dpy);
  227.     av_log(s1, AV_LOG_INFO, "shared memory extension%s found\n", use_shm ? "" : " not");
  228.  
  229.     if(use_shm) {
  230.         int scr = XDefaultScreen(dpy);
  231.         image = XShmCreateImage(dpy,
  232.                                 DefaultVisual(dpy, scr),
  233.                                 DefaultDepth(dpy, scr),
  234.                                 ZPixmap,
  235.                                 NULL,
  236.                                 &x11grab->shminfo,
  237.                                 x11grab->width, x11grab->height);
  238.         x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
  239.                                         image->bytes_per_line * image->height,
  240.                                         IPC_CREAT|0777);
  241.         if (x11grab->shminfo.shmid == -1) {
  242.             av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
  243.             ret = AVERROR(ENOMEM);
  244.             goto out;
  245.         }
  246.         x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
  247.         x11grab->shminfo.readOnly = False;
  248.  
  249.         if (!XShmAttach(dpy, &x11grab->shminfo)) {
  250.             av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
  251.             /* needs some better error subroutine :) */
  252.             ret = AVERROR(EIO);
  253.             goto out;
  254.         }
  255.     } else {
  256.         image = XGetImage(dpy, RootWindow(dpy, screen),
  257.                           x_off,y_off,
  258.                           x11grab->width, x11grab->height,
  259.                           AllPlanes, ZPixmap);
  260.     }
  261.  
  262.     switch (image->bits_per_pixel) {
  263.     case 8:
  264.         av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
  265.         input_pixfmt = AV_PIX_FMT_PAL8;
  266.         color_map = DefaultColormap(dpy, screen);
  267.         for (i = 0; i < 256; ++i)
  268.             color[i].pixel = i;
  269.         XQueryColors(dpy, color_map, color, 256);
  270.         for (i = 0; i < 256; ++i)
  271.             x11grab->palette[i] = (color[i].red   & 0xFF00) << 8 |
  272.                                   (color[i].green & 0xFF00)      |
  273.                                   (color[i].blue  & 0xFF00) >> 8;
  274.         x11grab->palette_changed = 1;
  275.         break;
  276.     case 16:
  277.         if (       image->red_mask   == 0xf800 &&
  278.                    image->green_mask == 0x07e0 &&
  279.                    image->blue_mask  == 0x001f ) {
  280.             av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
  281.             input_pixfmt = AV_PIX_FMT_RGB565;
  282.         } else if (image->red_mask   == 0x7c00 &&
  283.                    image->green_mask == 0x03e0 &&
  284.                    image->blue_mask  == 0x001f ) {
  285.             av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
  286.             input_pixfmt = AV_PIX_FMT_RGB555;
  287.         } else {
  288.             av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
  289.             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);
  290.             ret = AVERROR(EIO);
  291.             goto out;
  292.         }
  293.         break;
  294.     case 24:
  295.         if (        image->red_mask   == 0xff0000 &&
  296.                     image->green_mask == 0x00ff00 &&
  297.                     image->blue_mask  == 0x0000ff ) {
  298.             input_pixfmt = AV_PIX_FMT_BGR24;
  299.         } else if ( image->red_mask   == 0x0000ff &&
  300.                     image->green_mask == 0x00ff00 &&
  301.                     image->blue_mask  == 0xff0000 ) {
  302.             input_pixfmt = AV_PIX_FMT_RGB24;
  303.         } else {
  304.             av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
  305.             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);
  306.             ret = AVERROR(EIO);
  307.             goto out;
  308.         }
  309.         break;
  310.     case 32:
  311.         input_pixfmt = AV_PIX_FMT_0RGB32;
  312.         break;
  313.     default:
  314.         av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
  315.         ret = AVERROR(EINVAL);
  316.         goto out;
  317.     }
  318.  
  319.     x11grab->frame_size = x11grab->width * x11grab->height * image->bits_per_pixel/8;
  320.     x11grab->dpy = dpy;
  321.     x11grab->time_base  = av_inv_q(x11grab->framerate);
  322.     x11grab->time_frame = av_gettime() / av_q2d(x11grab->time_base);
  323.     x11grab->x_off = x_off;
  324.     x11grab->y_off = y_off;
  325.     x11grab->image = image;
  326.     x11grab->use_shm = use_shm;
  327.  
  328.     st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
  329.     st->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
  330.     st->codec->width  = x11grab->width;
  331.     st->codec->height = x11grab->height;
  332.     st->codec->pix_fmt = input_pixfmt;
  333.     st->codec->time_base = x11grab->time_base;
  334.     st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(x11grab->time_base) * 8;
  335.  
  336. out:
  337.     av_free(dpyname);
  338.     return ret;
  339. }
  340.  
  341. /**
  342.  * Paint a mouse pointer in an X11 image.
  343.  *
  344.  * @param image image to paint the mouse pointer to
  345.  * @param s context used to retrieve original grabbing rectangle
  346.  *          coordinates
  347.  */
  348. static void
  349. paint_mouse_pointer(XImage *image, struct x11grab *s)
  350. {
  351.     int x_off = s->x_off;
  352.     int y_off = s->y_off;
  353.     int width = s->width;
  354.     int height = s->height;
  355.     Display *dpy = s->dpy;
  356.     XFixesCursorImage *xcim;
  357.     int x, y;
  358.     int line, column;
  359.     int to_line, to_column;
  360.     int pixstride = image->bits_per_pixel >> 3;
  361.     /* Warning: in its insanity, xlib provides unsigned image data through a
  362.      * char* pointer, so we have to make it uint8_t to make things not break.
  363.      * Anyone who performs further investigation of the xlib API likely risks
  364.      * permanent brain damage. */
  365.     uint8_t *pix = image->data;
  366.     Window w;
  367.     XSetWindowAttributes attr;
  368.  
  369.     /* Code doesn't currently support 16-bit or PAL8 */
  370.     if (image->bits_per_pixel != 24 && image->bits_per_pixel != 32)
  371.         return;
  372.  
  373.     if(!s->c)
  374.         s->c = XCreateFontCursor(dpy, XC_left_ptr);
  375.     w = DefaultRootWindow(dpy);
  376.     attr.cursor = s->c;
  377.     XChangeWindowAttributes(dpy, w, CWCursor, &attr);
  378.  
  379.     xcim = XFixesGetCursorImage(dpy);
  380.  
  381.     x = xcim->x - xcim->xhot;
  382.     y = xcim->y - xcim->yhot;
  383.  
  384.     to_line = FFMIN((y + xcim->height), (height + y_off));
  385.     to_column = FFMIN((x + xcim->width), (width + x_off));
  386.  
  387.     for (line = FFMAX(y, y_off); line < to_line; line++) {
  388.         for (column = FFMAX(x, x_off); column < to_column; column++) {
  389.             int  xcim_addr = (line - y) * xcim->width + column - x;
  390.             int image_addr = ((line - y_off) * width + column - x_off) * pixstride;
  391.             int r = (uint8_t)(xcim->pixels[xcim_addr] >>  0);
  392.             int g = (uint8_t)(xcim->pixels[xcim_addr] >>  8);
  393.             int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16);
  394.             int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24);
  395.  
  396.             if (a == 255) {
  397.                 pix[image_addr+0] = r;
  398.                 pix[image_addr+1] = g;
  399.                 pix[image_addr+2] = b;
  400.             } else if (a) {
  401.                 /* pixel values from XFixesGetCursorImage come premultiplied by alpha */
  402.                 pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255;
  403.                 pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255;
  404.                 pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255;
  405.             }
  406.         }
  407.     }
  408.  
  409.     XFree(xcim);
  410.     xcim = NULL;
  411. }
  412.  
  413.  
  414. /**
  415.  * Read new data in the image structure.
  416.  *
  417.  * @param dpy X11 display to grab from
  418.  * @param d
  419.  * @param image Image where the grab will be put
  420.  * @param x Top-Left grabbing rectangle horizontal coordinate
  421.  * @param y Top-Left grabbing rectangle vertical coordinate
  422.  * @return 0 if error, !0 if successful
  423.  */
  424. static int
  425. xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
  426. {
  427.     xGetImageReply rep;
  428.     xGetImageReq *req;
  429.     long nbytes;
  430.  
  431.     if (!image) {
  432.         return 0;
  433.     }
  434.  
  435.     LockDisplay(dpy);
  436.     GetReq(GetImage, req);
  437.  
  438.     /* First set up the standard stuff in the request */
  439.     req->drawable = d;
  440.     req->x = x;
  441.     req->y = y;
  442.     req->width = image->width;
  443.     req->height = image->height;
  444.     req->planeMask = (unsigned int)AllPlanes;
  445.     req->format = ZPixmap;
  446.  
  447.     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
  448.         UnlockDisplay(dpy);
  449.         SyncHandle();
  450.         return 0;
  451.     }
  452.  
  453.     nbytes = (long)rep.length << 2;
  454.     _XReadPad(dpy, image->data, nbytes);
  455.  
  456.     UnlockDisplay(dpy);
  457.     SyncHandle();
  458.     return 1;
  459. }
  460.  
  461. /**
  462.  * Grab a frame from x11 (public device demuxer API).
  463.  *
  464.  * @param s1 Context from avformat core
  465.  * @param pkt Packet holding the brabbed frame
  466.  * @return frame size in bytes
  467.  */
  468. static int
  469. x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
  470. {
  471.     struct x11grab *s = s1->priv_data;
  472.     Display *dpy = s->dpy;
  473.     XImage *image = s->image;
  474.     int x_off = s->x_off;
  475.     int y_off = s->y_off;
  476.  
  477.     int screen;
  478.     Window root;
  479.     int follow_mouse = s->follow_mouse;
  480.  
  481.     int64_t curtime, delay;
  482.     struct timespec ts;
  483.  
  484.     /* Calculate the time of the next frame */
  485.     s->time_frame += INT64_C(1000000);
  486.  
  487.     /* wait based on the frame rate */
  488.     for(;;) {
  489.         curtime = av_gettime();
  490.         delay = s->time_frame * av_q2d(s->time_base) - curtime;
  491.         if (delay <= 0) {
  492.             if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
  493.                 s->time_frame += INT64_C(1000000);
  494.             }
  495.             break;
  496.         }
  497.         ts.tv_sec = delay / 1000000;
  498.         ts.tv_nsec = (delay % 1000000) * 1000;
  499.         nanosleep(&ts, NULL);
  500.     }
  501.  
  502.     av_init_packet(pkt);
  503.     pkt->data = image->data;
  504.     pkt->size = s->frame_size;
  505.     pkt->pts = curtime;
  506.     if (s->palette_changed) {
  507.         uint8_t *pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE,
  508.                                                AVPALETTE_SIZE);
  509.         if (!pal) {
  510.             av_log(s, AV_LOG_ERROR, "Cannot append palette to packet\n");
  511.         } else {
  512.             memcpy(pal, s->palette, AVPALETTE_SIZE);
  513.             s->palette_changed = 0;
  514.         }
  515.     }
  516.  
  517.     screen = DefaultScreen(dpy);
  518.     root = RootWindow(dpy, screen);
  519.     if (follow_mouse) {
  520.         int screen_w, screen_h;
  521.         int pointer_x, pointer_y, _;
  522.         Window w;
  523.  
  524.         screen_w = DisplayWidth(dpy, screen);
  525.         screen_h = DisplayHeight(dpy, screen);
  526.         XQueryPointer(dpy, root, &w, &w, &pointer_x, &pointer_y, &_, &_, &_);
  527.         if (follow_mouse == -1) {
  528.             // follow the mouse, put it at center of grabbing region
  529.             x_off += pointer_x - s->width  / 2 - x_off;
  530.             y_off += pointer_y - s->height / 2 - y_off;
  531.         } else {
  532.             // follow the mouse, but only move the grabbing region when mouse
  533.             // reaches within certain pixels to the edge.
  534.             if (pointer_x > x_off + s->width - follow_mouse) {
  535.                 x_off += pointer_x - (x_off + s->width - follow_mouse);
  536.             } else if (pointer_x < x_off + follow_mouse)
  537.                 x_off -= (x_off + follow_mouse) - pointer_x;
  538.             if (pointer_y > y_off + s->height - follow_mouse) {
  539.                 y_off += pointer_y - (y_off + s->height - follow_mouse);
  540.             } else if (pointer_y < y_off + follow_mouse)
  541.                 y_off -= (y_off + follow_mouse) - pointer_y;
  542.         }
  543.         // adjust grabbing region position if it goes out of screen.
  544.         s->x_off = x_off = FFMIN(FFMAX(x_off, 0), screen_w - s->width);
  545.         s->y_off = y_off = FFMIN(FFMAX(y_off, 0), screen_h - s->height);
  546.  
  547.         if (s->show_region && s->region_win)
  548.             XMoveWindow(dpy, s->region_win,
  549.                         s->x_off - REGION_WIN_BORDER,
  550.                         s->y_off - REGION_WIN_BORDER);
  551.     }
  552.  
  553.     if (s->show_region) {
  554.         if (s->region_win) {
  555.             XEvent evt;
  556.             // clean up the events, and do the initinal draw or redraw.
  557.             for (evt.type = NoEventMask; XCheckMaskEvent(dpy, ExposureMask | StructureNotifyMask, &evt); );
  558.             if (evt.type)
  559.                 x11grab_draw_region_win(s);
  560.         } else {
  561.             x11grab_region_win_init(s);
  562.         }
  563.     }
  564.  
  565.     if(s->use_shm) {
  566.         if (!XShmGetImage(dpy, root, image, x_off, y_off, AllPlanes)) {
  567.             av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
  568.         }
  569.     } else {
  570.         if (!xget_zpixmap(dpy, root, image, x_off, y_off)) {
  571.             av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
  572.         }
  573.     }
  574.  
  575.     if (s->draw_mouse) {
  576.         paint_mouse_pointer(image, s);
  577.     }
  578.  
  579.     return s->frame_size;
  580. }
  581.  
  582. /**
  583.  * Close x11 frame grabber (public device demuxer API).
  584.  *
  585.  * @param s1 Context from avformat core
  586.  * @return 0 success, !0 failure
  587.  */
  588. static int
  589. x11grab_read_close(AVFormatContext *s1)
  590. {
  591.     struct x11grab *x11grab = s1->priv_data;
  592.  
  593.     /* Detach cleanly from shared mem */
  594.     if (x11grab->use_shm) {
  595.         XShmDetach(x11grab->dpy, &x11grab->shminfo);
  596.         shmdt(x11grab->shminfo.shmaddr);
  597.         shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL);
  598.     }
  599.  
  600.     /* Destroy X11 image */
  601.     if (x11grab->image) {
  602.         XDestroyImage(x11grab->image);
  603.         x11grab->image = NULL;
  604.     }
  605.  
  606.     if (x11grab->region_win) {
  607.         XDestroyWindow(x11grab->dpy, x11grab->region_win);
  608.     }
  609.  
  610.     /* Free X11 display */
  611.     XCloseDisplay(x11grab->dpy);
  612.     return 0;
  613. }
  614.  
  615. #define OFFSET(x) offsetof(struct x11grab, x)
  616. #define DEC AV_OPT_FLAG_DECODING_PARAM
  617. static const AVOption options[] = {
  618.     { "draw_mouse", "draw the mouse pointer", OFFSET(draw_mouse), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, DEC },
  619.  
  620.     { "follow_mouse", "move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region",
  621.       OFFSET(follow_mouse), AV_OPT_TYPE_INT, {.i64 = 0}, -1, INT_MAX, DEC, "follow_mouse" },
  622.     { "centered",     "keep the mouse pointer at the center of grabbing region when following",
  623.       0, AV_OPT_TYPE_CONST, {.i64 = -1}, INT_MIN, INT_MAX, DEC, "follow_mouse" },
  624.  
  625.     { "framerate",  "set video frame rate",      OFFSET(framerate),   AV_OPT_TYPE_VIDEO_RATE, {.str = "ntsc"}, 0, 0, DEC },
  626.     { "show_region", "show the grabbing region", OFFSET(show_region), AV_OPT_TYPE_INT,        {.i64 = 0}, 0, 1, DEC },
  627.     { "video_size",  "set video frame size",     OFFSET(width),       AV_OPT_TYPE_IMAGE_SIZE, {.str = "vga"}, 0, 0, DEC },
  628.     { NULL },
  629. };
  630.  
  631. static const AVClass x11_class = {
  632.     .class_name = "X11grab indev",
  633.     .item_name  = av_default_item_name,
  634.     .option     = options,
  635.     .version    = LIBAVUTIL_VERSION_INT,
  636. };
  637.  
  638. /** x11 grabber device demuxer declaration */
  639. AVInputFormat ff_x11grab_demuxer = {
  640.     .name           = "x11grab",
  641.     .long_name      = NULL_IF_CONFIG_SMALL("X11grab"),
  642.     .priv_data_size = sizeof(struct x11grab),
  643.     .read_header    = x11grab_read_header,
  644.     .read_packet    = x11grab_read_packet,
  645.     .read_close     = x11grab_read_close,
  646.     .flags          = AVFMT_NOFILE,
  647.     .priv_class     = &x11_class,
  648. };
  649.