x11grab.c 20.6 KB
Newer Older
1 2
/*
 * X11 video grab interface
3
 *
4
 * This file is part of Libav.
5
 *
6
 * Libav integration:
7 8
 * Copyright (C) 2006 Clemens Fruhwirth <clemens@endorphin.org>
 *                    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 15
 * Copyright (C) 1997-1998 Rasca, Berlin
 *               2003-2004 Karl H. Beckers, Frankfurt
16
 *
17
 * Libav is free software; you can redistribute it and/or modify
18 19 20 21
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
22
 * Libav is distributed in the hope that it will be useful,
23 24 25 26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
27
 * You should have received a copy of the GNU General Public License
28
 * along with Libav; 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 35 36 37
 * X11 frame device demuxer by Clemens Fruhwirth <clemens@endorphin.org>
 * and Edouard Gomez <ed.gomez@free.fr>.
 */

38
#include "config.h"
39
#include "libavformat/avformat.h"
40 41 42
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
43 44 45 46 47
#include <time.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#include <X11/Xproto.h>
48
#include <X11/Xutil.h>
49
#include <sys/shm.h>
50
#include <X11/extensions/shape.h>
51
#include <X11/extensions/XShm.h>
Roxis's avatar
Roxis committed
52
#include <X11/extensions/Xfixes.h>
53

54 55 56
/**
 * X11 Device Demuxer context
 */
57
struct x11_grab
58
{
59
    const AVClass *class;    /**< Class for private options. */
60 61 62 63
    int frame_size;          /**< Size in bytes of a grabbed frame */
    AVRational time_base;    /**< Time base */
    int64_t time_frame;      /**< Current time */

64
    char *video_size;        /**< String describing video size, set by a private option. */
65 66 67 68 69 70 71 72 73
    int height;              /**< Height of the grab frame */
    int width;               /**< Width of the grab frame */
    int x_off;               /**< Horizontal top-left corner coordinate */
    int y_off;               /**< Vertical top-left corner coordinate */

    Display *dpy;            /**< X11 display from which x11grab grabs frames */
    XImage *image;           /**< X11 image holding the grab */
    int use_shm;             /**< !0 when using XShm extension */
    XShmSegmentInfo shminfo; /**< When using XShm, keeps track of XShm infos */
74
    int  draw_mouse;         /**< Set by a private option. */
75
    int  follow_mouse;       /**< Set by a private option. */
76
    int  show_region;        /**< set by a private option. */
77
    char *framerate;         /**< Set by a private option. */
78 79

    Window region_win;       /**< This is used by show_region option. */
80
};
81

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
#define REGION_WIN_BORDER 3
/**
 * Draw grabbing region window
 *
 * @param s x11_grab context
 */
static void
x11grab_draw_region_win(struct x11_grab *s)
{
    Display *dpy = s->dpy;
    int screen;
    Window win = s->region_win;
    GC gc;

    screen = DefaultScreen(dpy);
    gc = XCreateGC(dpy, win, 0, 0);
    XSetForeground(dpy, gc, WhitePixel(dpy, screen));
    XSetBackground(dpy, gc, BlackPixel(dpy, screen));
    XSetLineAttributes(dpy, gc, REGION_WIN_BORDER, LineDoubleDash, 0, 0);
    XDrawRectangle(dpy, win, gc,
                   1, 1,
                   (s->width  + REGION_WIN_BORDER * 2) - 1 * 2 - 1,
                   (s->height + REGION_WIN_BORDER * 2) - 1 * 2 - 1);
    XFreeGC(dpy, gc);
}

/**
 * Initialize grabbing region window
 *
 * @param s x11_grab context
 */
static void
x11grab_region_win_init(struct x11_grab *s)
{
    Display *dpy = s->dpy;
    int screen;
    XSetWindowAttributes attribs;
    XRectangle rect;

    screen = DefaultScreen(dpy);
    attribs.override_redirect = True;
    s->region_win = XCreateWindow(dpy, RootWindow(dpy, screen),
                                  s->x_off  - REGION_WIN_BORDER,
                                  s->y_off  - REGION_WIN_BORDER,
                                  s->width  + REGION_WIN_BORDER * 2,
                                  s->height + REGION_WIN_BORDER * 2,
                                  0, CopyFromParent,
                                  InputOutput, CopyFromParent,
                                  CWOverrideRedirect, &attribs);
    rect.x = 0;
    rect.y = 0;
    rect.width  = s->width;
    rect.height = s->height;
    XShapeCombineRectangles(dpy, s->region_win,
                            ShapeBounding, REGION_WIN_BORDER, REGION_WIN_BORDER,
                            &rect, 1, ShapeSubtract, 0);
    XMapWindow(dpy, s->region_win);
    XSelectInput(dpy, s->region_win, ExposureMask | StructureNotifyMask);
    x11grab_draw_region_win(s);
}

143
/**
144
 * Initialize the x11 grab device demuxer (public device demuxer API).
145 146 147 148
 *
 * @param s1 Context from avformat core
 * @param ap Parameters from avformat core
 * @return <ul>
149
 *          <li>AVERROR(ENOMEM) no memory left</li>
150
 *          <li>AVERROR(EIO) other failure case</li>
151 152 153
 *          <li>0 success</li>
 *         </ul>
 */
154 155
static int
x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
156
{
157
    struct x11_grab *x11grab = s1->priv_data;
158 159
    Display *dpy;
    AVStream *st = NULL;
160
    enum PixelFormat input_pixfmt;
161
    XImage *image;
162 163
    int x_off = 0;
    int y_off = 0;
164
    int screen;
165
    int use_shm;
166
    char *param, *offset;
167
    int ret = 0;
168
    AVRational framerate;
169

170
    param = av_strdup(s1->filename);
171 172 173
    offset = strchr(param, '+');
    if (offset) {
        sscanf(offset, "%d,%d", &x_off, &y_off);
174
        x11grab->draw_mouse = !strstr(offset, "nomouse");
175 176 177
        *offset= 0;
    }

178 179 180 181
    if ((ret = av_parse_video_size(&x11grab->width, &x11grab->height, x11grab->video_size)) < 0) {
        av_log(s1, AV_LOG_ERROR, "Couldn't parse video size.\n");
        goto out;
    }
182 183 184 185
    if ((ret = av_parse_video_rate(&framerate, x11grab->framerate)) < 0) {
        av_log(s1, AV_LOG_ERROR, "Could not parse framerate: %s.\n", x11grab->framerate);
        goto out;
    }
186 187
    av_log(s1, AV_LOG_INFO, "device: %s -> display: %s x: %d y: %d width: %d height: %d\n",
           s1->filename, param, x_off, y_off, x11grab->width, x11grab->height);
188 189 190 191

    dpy = XOpenDisplay(param);
    if(!dpy) {
        av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
192 193
        ret = AVERROR(EIO);
        goto out;
194
    }
195 196 197

    st = av_new_stream(s1, 0);
    if (!st) {
198 199
        ret = AVERROR(ENOMEM);
        goto out;
200 201 202
    }
    av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
    screen = DefaultScreen(dpy);

    if (x11grab->follow_mouse) {
        int screen_w, screen_h;
        Window w;

        screen_w = DisplayWidth(dpy, screen);
        screen_h = DisplayHeight(dpy, screen);
        XQueryPointer(dpy, RootWindow(dpy, screen), &w, &w, &x_off, &y_off, &ret, &ret, &ret);
        x_off -= x11grab->width / 2;
        y_off -= x11grab->height / 2;
        x_off = FFMIN(FFMAX(x_off, 0), screen_w - x11grab->width);
        y_off = FFMIN(FFMAX(y_off, 0), screen_h - x11grab->height);
        av_log(s1, AV_LOG_INFO, "followmouse is enabled, resetting grabbing region to x: %d y: %d\n", x_off, y_off);
    }

219
    use_shm = XShmQueryExtension(dpy);
220
    av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
221 222 223 224 225 226 227 228 229

    if(use_shm) {
        int scr = XDefaultScreen(dpy);
        image = XShmCreateImage(dpy,
                                DefaultVisual(dpy, scr),
                                DefaultDepth(dpy, scr),
                                ZPixmap,
                                NULL,
                                &x11grab->shminfo,
230
                                x11grab->width, x11grab->height);
231 232 233 234 235
        x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
                                        image->bytes_per_line * image->height,
                                        IPC_CREAT|0777);
        if (x11grab->shminfo.shmid == -1) {
            av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
236 237
            ret = AVERROR(ENOMEM);
            goto out;
238 239 240 241 242 243 244
        }
        x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
        x11grab->shminfo.readOnly = False;

        if (!XShmAttach(dpy, &x11grab->shminfo)) {
            av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
            /* needs some better error subroutine :) */
245 246
            ret = AVERROR(EIO);
            goto out;
247 248
        }
    } else {
249
        image = XGetImage(dpy, RootWindow(dpy, screen),
250
                          x_off,y_off,
251
                          x11grab->width, x11grab->height,
252 253 254 255 256
                          AllPlanes, ZPixmap);
    }

    switch (image->bits_per_pixel) {
    case 8:
257
        av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
258 259 260
        input_pixfmt = PIX_FMT_PAL8;
        break;
    case 16:
261 262 263
        if (       image->red_mask   == 0xf800 &&
                   image->green_mask == 0x07e0 &&
                   image->blue_mask  == 0x001f ) {
264 265
            av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
            input_pixfmt = PIX_FMT_RGB565;
266 267 268
        } else if (image->red_mask   == 0x7c00 &&
                   image->green_mask == 0x03e0 &&
                   image->blue_mask  == 0x001f ) {
269 270 271 272 273
            av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
            input_pixfmt = PIX_FMT_RGB555;
        } else {
            av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
            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);
274 275
            ret = AVERROR(EIO);
            goto out;
276 277 278
        }
        break;
    case 24:
279 280 281
        if (        image->red_mask   == 0xff0000 &&
                    image->green_mask == 0x00ff00 &&
                    image->blue_mask  == 0x0000ff ) {
282
            input_pixfmt = PIX_FMT_BGR24;
283 284 285
        } else if ( image->red_mask   == 0x0000ff &&
                    image->green_mask == 0x00ff00 &&
                    image->blue_mask  == 0xff0000 ) {
286 287 288 289
            input_pixfmt = PIX_FMT_RGB24;
        } else {
            av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
            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 291
            ret = AVERROR(EIO);
            goto out;
292 293 294
        }
        break;
    case 32:
295
        input_pixfmt = PIX_FMT_RGB32;
296 297 298
        break;
    default:
        av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
299 300
        ret = AVERROR(EINVAL);
        goto out;
301 302
    }

303
    x11grab->frame_size = x11grab->width * x11grab->height * image->bits_per_pixel/8;
304
    x11grab->dpy = dpy;
305 306
    x11grab->time_base  = (AVRational){framerate.den, framerate.num};
    x11grab->time_frame = av_gettime() / av_q2d(x11grab->time_base);
307 308 309 310 311
    x11grab->x_off = x_off;
    x11grab->y_off = y_off;
    x11grab->image = image;
    x11grab->use_shm = use_shm;

312
    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
313
    st->codec->codec_id = CODEC_ID_RAWVIDEO;
314 315
    st->codec->width  = x11grab->width;
    st->codec->height = x11grab->height;
316
    st->codec->pix_fmt = input_pixfmt;
317 318
    st->codec->time_base = x11grab->time_base;
    st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(x11grab->time_base) * 8;
319

320 321
out:
    return ret;
322 323
}

324
/**
325
 * Paint a mouse pointer in an X11 image.
326
 *
Diego Biurrun's avatar
Diego Biurrun committed
327
 * @param image image to paint the mouse pointer to
328 329 330
 * @param s context used to retrieve original grabbing rectangle
 *          coordinates
 */
331
static void
Roxis's avatar
Roxis committed
332
paint_mouse_pointer(XImage *image, struct x11_grab *s)
333
{
334 335 336 337
    int x_off = s->x_off;
    int y_off = s->y_off;
    int width = s->width;
    int height = s->height;
Roxis's avatar
Roxis committed
338 339 340 341 342
    Display *dpy = s->dpy;
    XFixesCursorImage *xcim;
    int x, y;
    int line, column;
    int to_line, to_column;
343 344 345 346 347 348 349 350 351 352
    int pixstride = image->bits_per_pixel >> 3;
    /* Warning: in its insanity, xlib provides unsigned image data through a
     * char* pointer, so we have to make it uint8_t to make things not break.
     * Anyone who performs further investigation of the xlib API likely risks
     * permanent brain damage. */
    uint8_t *pix = image->data;

    /* Code doesn't currently support 16-bit or PAL8 */
    if (image->bits_per_pixel != 24 && image->bits_per_pixel != 32)
        return;
353

354
    xcim = XFixesGetCursorImage(dpy);
355

Roxis's avatar
Roxis committed
356 357 358 359 360 361 362 363
    x = xcim->x - xcim->xhot;
    y = xcim->y - xcim->yhot;

    to_line = FFMIN((y + xcim->height), (height + y_off));
    to_column = FFMIN((x + xcim->width), (width + x_off));

    for (line = FFMAX(y, y_off); line < to_line; line++) {
        for (column = FFMAX(x, x_off); column < to_column; column++) {
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
            int  xcim_addr = (line - y) * xcim->width + column - x;
            int image_addr = ((line - y_off) * width + column - x_off) * pixstride;
            int r = (uint8_t)(xcim->pixels[xcim_addr] >>  0);
            int g = (uint8_t)(xcim->pixels[xcim_addr] >>  8);
            int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16);
            int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24);

            if (a == 255) {
                pix[image_addr+0] = r;
                pix[image_addr+1] = g;
                pix[image_addr+2] = b;
            } else if (a) {
                /* pixel values from XFixesGetCursorImage come premultiplied by alpha */
                pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255;
                pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255;
                pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255;
380
            }
381 382
        }
    }
Roxis's avatar
Roxis committed
383 384 385

    XFree(xcim);
    xcim = NULL;
386 387 388
}


389
/**
390
 * Read new data in the image structure.
391 392
 *
 * @param dpy X11 display to grab from
393
 * @param d
394 395 396 397
 * @param image Image where the grab will be put
 * @param x Top-Left grabbing rectangle horizontal coordinate
 * @param y Top-Left grabbing rectangle vertical coordinate
 * @return 0 if error, !0 if successful
398 399
 */
static int
400
xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
401
{
402 403 404 405 406
    xGetImageReply rep;
    xGetImageReq *req;
    long nbytes;

    if (!image) {
407
        return 0;
408 409 410 411 412 413 414 415 416 417 418 419 420 421
    }

    LockDisplay(dpy);
    GetReq(GetImage, req);

    /* First set up the standard stuff in the request */
    req->drawable = d;
    req->x = x;
    req->y = y;
    req->width = image->width;
    req->height = image->height;
    req->planeMask = (unsigned int)AllPlanes;
    req->format = ZPixmap;

422
    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
423 424
        UnlockDisplay(dpy);
        SyncHandle();
425
        return 0;
426 427 428 429 430 431 432
    }

    nbytes = (long)rep.length << 2;
    _XReadPad(dpy, image->data, nbytes);

    UnlockDisplay(dpy);
    SyncHandle();
433
    return 1;
434 435
}

436
/**
437
 * Grab a frame from x11 (public device demuxer API).
438 439 440 441 442
 *
 * @param s1 Context from avformat core
 * @param pkt Packet holding the brabbed frame
 * @return frame size in bytes
 */
443 444
static int
x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
445
{
446
    struct x11_grab *s = s1->priv_data;
447 448 449 450 451
    Display *dpy = s->dpy;
    XImage *image = s->image;
    int x_off = s->x_off;
    int y_off = s->y_off;

452 453 454 455
    int screen;
    Window root;
    int follow_mouse = s->follow_mouse;

456 457 458 459
    int64_t curtime, delay;
    struct timespec ts;

    /* Calculate the time of the next frame */
460
    s->time_frame += INT64_C(1000000);
461 462 463 464

    /* wait based on the frame rate */
    for(;;) {
        curtime = av_gettime();
465
        delay = s->time_frame * av_q2d(s->time_base) - curtime;
466
        if (delay <= 0) {
467 468
            if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
                s->time_frame += INT64_C(1000000);
469 470 471 472 473 474 475 476
            }
            break;
        }
        ts.tv_sec = delay / 1000000;
        ts.tv_nsec = (delay % 1000000) * 1000;
        nanosleep(&ts, NULL);
    }

477 478 479
    av_init_packet(pkt);
    pkt->data = image->data;
    pkt->size = s->frame_size;
480
    pkt->pts = curtime;
481

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
    screen = DefaultScreen(dpy);
    root = RootWindow(dpy, screen);
    if (follow_mouse) {
        int screen_w, screen_h;
        int pointer_x, pointer_y, _;
        Window w;

        screen_w = DisplayWidth(dpy, screen);
        screen_h = DisplayHeight(dpy, screen);
        XQueryPointer(dpy, root, &w, &w, &pointer_x, &pointer_y, &_, &_, &_);
        if (follow_mouse == -1) {
            // follow the mouse, put it at center of grabbing region
            x_off += pointer_x - s->width  / 2 - x_off;
            y_off += pointer_y - s->height / 2 - y_off;
        } else {
            // follow the mouse, but only move the grabbing region when mouse
            // reaches within certain pixels to the edge.
            if (pointer_x > x_off + s->width - follow_mouse) {
                x_off += pointer_x - (x_off + s->width - follow_mouse);
            } else if (pointer_x < x_off + follow_mouse)
                x_off -= (x_off + follow_mouse) - pointer_x;
            if (pointer_y > y_off + s->height - follow_mouse) {
                y_off += pointer_y - (y_off + s->height - follow_mouse);
            } else if (pointer_y < y_off + follow_mouse)
                y_off -= (y_off + follow_mouse) - pointer_y;
        }
        // adjust grabbing region position if it goes out of screen.
        s->x_off = x_off = FFMIN(FFMAX(x_off, 0), screen_w - s->width);
        s->y_off = y_off = FFMIN(FFMAX(y_off, 0), screen_h - s->height);
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527

        if (s->show_region && s->region_win)
            XMoveWindow(dpy, s->region_win,
                        s->x_off - REGION_WIN_BORDER,
                        s->y_off - REGION_WIN_BORDER);
    }

    if (s->show_region) {
        if (s->region_win) {
            XEvent evt;
            // clean up the events, and do the initinal draw or redraw.
            for (evt.type = NoEventMask; XCheckMaskEvent(dpy, ExposureMask | StructureNotifyMask, &evt); );
            if (evt.type)
                x11grab_draw_region_win(s);
        } else {
            x11grab_region_win_init(s);
        }
528 529
    }

530
    if(s->use_shm) {
531
        if (!XShmGetImage(dpy, root, image, x_off, y_off, AllPlanes)) {
532 533 534
            av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
        }
    } else {
535
        if (!xget_zpixmap(dpy, root, image, x_off, y_off)) {
536 537 538 539
            av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
        }
    }

540
    if (s->draw_mouse) {
Roxis's avatar
Roxis committed
541
        paint_mouse_pointer(image, s);
542 543 544
    }

    return s->frame_size;
545 546
}

547
/**
548
 * Close x11 frame grabber (public device demuxer API).
549 550 551 552
 *
 * @param s1 Context from avformat core
 * @return 0 success, !0 failure
 */
553 554
static int
x11grab_read_close(AVFormatContext *s1)
555
{
556
    struct x11_grab *x11grab = s1->priv_data;
557 558 559 560 561 562 563 564 565 566 567 568 569 570

    /* Detach cleanly from shared mem */
    if (x11grab->use_shm) {
        XShmDetach(x11grab->dpy, &x11grab->shminfo);
        shmdt(x11grab->shminfo.shmaddr);
        shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL);
    }

    /* Destroy X11 image */
    if (x11grab->image) {
        XDestroyImage(x11grab->image);
        x11grab->image = NULL;
    }

571 572 573 574
    if (x11grab->region_win) {
        XDestroyWindow(x11grab->dpy, x11grab->region_win);
    }

575 576 577
    /* Free X11 display */
    XCloseDisplay(x11grab->dpy);
    return 0;
578 579
}

580 581 582 583
#define OFFSET(x) offsetof(struct x11_grab, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
    { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), FF_OPT_TYPE_STRING, {.str = "vga"}, 0, 0, DEC },
584
    { "framerate", "", OFFSET(framerate), FF_OPT_TYPE_STRING, {.str = "ntsc"}, 0, 0, DEC },
585
    { "draw_mouse", "Draw the mouse pointer.", OFFSET(draw_mouse), FF_OPT_TYPE_INT, { 1 }, 0, 1, DEC },
586 587 588
    { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
      OFFSET(follow_mouse), FF_OPT_TYPE_INT, { 0 }, -1, INT_MAX, DEC, "follow_mouse" },
    { "centered", "Keep the mouse pointer at the center of grabbing region when following.", 0, FF_OPT_TYPE_CONST, { -1 }, INT_MIN, INT_MAX, DEC, "follow_mouse" },
589
    { "show_region", "Show the grabbing region.", OFFSET(show_region), FF_OPT_TYPE_INT, { 0 }, 0, 1, DEC },
590 591 592 593 594 595 596 597 598 599
    { NULL },
};

static const AVClass x11_class = {
    .class_name = "X11grab indev",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

600
/** x11 grabber device demuxer declaration */
601 602 603 604 605 606 607 608 609
AVInputFormat ff_x11_grab_device_demuxer = {
    .name           = "x11grab",
    .long_name      = NULL_IF_CONFIG_SMALL("X11grab"),
    .priv_data_size = sizeof(struct x11_grab),
    .read_header    = x11grab_read_header,
    .read_packet    = x11grab_read_packet,
    .read_close     = x11grab_read_close,
    .flags          = AVFMT_NOFILE,
    .priv_class     = &x11_class,
610
};