x11grab.c 21.4 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

Edouard Gomez's avatar
Edouard Gomez committed
32
/**
33
 * @file
34 35 36
 * X11 frame device demuxer
 * @author Clemens Fruhwirth <clemens@endorphin.org>
 * @author Edouard Gomez <ed.gomez@free.fr>
Edouard Gomez's avatar
Edouard Gomez committed
37 38
 */

39
#include "config.h"
40

41
#include <time.h>
42 43
#include <sys/shm.h>

44 45 46 47
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#include <X11/Xproto.h>
48
#include <X11/Xutil.h>
49

50
#include <X11/extensions/shape.h>
Roxis's avatar
Roxis committed
51
#include <X11/extensions/Xfixes.h>
52
#include <X11/extensions/XShm.h>
53

54
#include "libavutil/internal.h"
55 56 57 58 59 60 61 62 63
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/time.h"

#include "libavformat/avformat.h"
#include "libavformat/internal.h"

/** X11 device demuxer context */
64
typedef struct X11GrabContext {
65
    const AVClass *class;    /**< Class for private options. */
Edouard Gomez's avatar
Edouard Gomez committed
66 67 68 69
    int frame_size;          /**< Size in bytes of a grabbed frame */
    AVRational time_base;    /**< Time base */
    int64_t time_frame;      /**< Current time */

70
    char *video_size;        /**< String describing video size, set by a private option. */
Edouard Gomez's avatar
Edouard Gomez committed
71 72 73 74 75 76 77 78 79
    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 */
80 81 82
    int draw_mouse;          /**< Set by a private option. */
    int follow_mouse;        /**< Set by a private option. */
    int show_region;         /**< set by a private option. */
83
    char *framerate;         /**< Set by a private option. */
84 85

    Window region_win;       /**< This is used by show_region option. */
86
} X11GrabContext;
Edouard Gomez's avatar
Edouard Gomez committed
87

88
#define REGION_WIN_BORDER 3
89

90 91 92
/**
 * Draw grabbing region window
 *
93
 * @param s x11grab context
94
 */
95
static void x11grab_draw_region_win(X11GrabContext *s)
96 97
{
    Display *dpy = s->dpy;
98
    Window win   = s->region_win;
99 100
    int screen = DefaultScreen(dpy);
    GC gc = XCreateGC(dpy, win, 0, 0);
101 102 103 104

    XSetForeground(dpy, gc, WhitePixel(dpy, screen));
    XSetBackground(dpy, gc, BlackPixel(dpy, screen));
    XSetLineAttributes(dpy, gc, REGION_WIN_BORDER, LineDoubleDash, 0, 0);
105
    XDrawRectangle(dpy, win, gc, 1, 1,
106 107 108 109 110 111 112 113
                   (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
 *
114
 * @param s x11grab context
115
 */
116
static void x11grab_region_win_init(X11GrabContext *s)
117 118 119
{
    Display *dpy = s->dpy;
    XRectangle rect;
120 121
    XSetWindowAttributes attribs = { .override_redirect = True };
    int screen = DefaultScreen(dpy);
122 123 124 125 126 127 128 129 130

    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);
131 132
    rect.x      = 0;
    rect.y      = 0;
133 134 135 136 137 138 139 140 141 142
    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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
static int setup_shm(AVFormatContext *s, Display *dpy, XImage **image)
{
    X11GrabContext *g = s->priv_data;
    int scr           = XDefaultScreen(dpy);
    XImage *img       = XShmCreateImage(dpy, DefaultVisual(dpy, scr),
                                        DefaultDepth(dpy, scr), ZPixmap, NULL,
                                        &g->shminfo, g->width, g->height);

    g->shminfo.shmid = shmget(IPC_PRIVATE, img->bytes_per_line * img->height,
                              IPC_CREAT | 0777);

    if (g->shminfo.shmid == -1) {
        av_log(s, AV_LOG_ERROR, "Cannot get shared memory!\n");
        return AVERROR(ENOMEM);
    }

    g->shminfo.shmaddr  = img->data = shmat(g->shminfo.shmid, 0, 0);
    g->shminfo.readOnly = False;

    if (!XShmAttach(dpy, &g->shminfo)) {
        av_log(s, AV_LOG_ERROR, "Failed to attach shared memory!\n");
        /* needs some better error subroutine :) */
        return AVERROR(EIO);
    }

    *image = img;
    return 0;
}

172 173 174 175 176 177 178 179 180 181 182 183 184
static int setup_mouse(Display *dpy, int screen)
{
    int ev_ret, ev_err;

    if (XFixesQueryExtension(dpy, &ev_ret, &ev_err)) {
        Window root = RootWindow(dpy, screen);
        XFixesSelectCursorInput(dpy, root, XFixesDisplayCursorNotifyMask);
        return 0;
    }

    return AVERROR(ENOSYS);
}

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
static int pixfmt_from_image(AVFormatContext *s, XImage *image, int *pix_fmt)
{
    av_log(s, AV_LOG_DEBUG,
           "Image r 0x%.6lx g 0x%.6lx b 0x%.6lx and depth %i\n",
           image->red_mask,
           image->green_mask,
           image->blue_mask,
           image->bits_per_pixel);

    switch (image->bits_per_pixel) {
    case 8:
        *pix_fmt =  AV_PIX_FMT_PAL8;
        break;
    case 16:
        if (image->red_mask   == 0xf800 &&
            image->green_mask == 0x07e0 &&
            image->blue_mask  == 0x001f) {
            *pix_fmt = AV_PIX_FMT_RGB565;
        } else if (image->red_mask   == 0x7c00 &&
                   image->green_mask == 0x03e0 &&
                   image->blue_mask  == 0x001f) {
            *pix_fmt = AV_PIX_FMT_RGB555;
        }
        break;
    case 24:
        if (image->red_mask   == 0xff0000 &&
            image->green_mask == 0x00ff00 &&
            image->blue_mask  == 0x0000ff) {
            *pix_fmt = AV_PIX_FMT_BGR24;
        } else if (image->red_mask   == 0x0000ff &&
                   image->green_mask == 0x00ff00 &&
                   image->blue_mask  == 0xff0000) {
            *pix_fmt = AV_PIX_FMT_RGB24;
        }
        break;
    case 32:
        *pix_fmt = AV_PIX_FMT_RGB32;
        break;
    default:
        av_log(s, AV_LOG_ERROR,
               "XImages with RGB mask 0x%.6lx 0x%.6lx 0x%.6lx and depth %i "
               "are currently not supported.\n",
               image->red_mask,
               image->green_mask,
               image->blue_mask,
               image->bits_per_pixel);

        return AVERROR_PATCHWELCOME;
    }

    return 0;
}

Edouard Gomez's avatar
Edouard Gomez committed
238
/**
239
 * Initialize the x11 grab device demuxer (public device demuxer API).
Edouard Gomez's avatar
Edouard Gomez committed
240 241 242
 *
 * @param s1 Context from avformat core
 * @return <ul>
243
 *          <li>AVERROR(ENOMEM) no memory left</li>
244
 *          <li>AVERROR(EIO) other failure case</li>
Edouard Gomez's avatar
Edouard Gomez committed
245 246 247
 *          <li>0 success</li>
 *         </ul>
 */
248
static int x11grab_read_header(AVFormatContext *s1)
249
{
250
    X11GrabContext *x11grab = s1->priv_data;
251 252 253
    Display *dpy;
    AVStream *st = NULL;
    XImage *image;
254
    int x_off = 0, y_off = 0, ret = 0, screen, use_shm;
255
    char *param, *offset;
256
    AVRational framerate;
257

258
    param = av_strdup(s1->filename);
259 260 261
    if (!param)
        goto out;

262 263 264
    offset = strchr(param, '+');
    if (offset) {
        sscanf(offset, "%d,%d", &x_off, &y_off);
265
        x11grab->draw_mouse = !strstr(offset, "nomouse");
266
        *offset = 0;
267 268
    }

269 270 271
    ret = av_parse_video_size(&x11grab->width, &x11grab->height,
                              x11grab->video_size);
    if (ret < 0) {
272 273 274
        av_log(s1, AV_LOG_ERROR, "Couldn't parse video size.\n");
        goto out;
    }
275 276 277

    ret = av_parse_video_rate(&framerate, x11grab->framerate);
    if (ret < 0) {
278 279
        av_log(s1, AV_LOG_ERROR, "Could not parse framerate: %s.\n",
               x11grab->framerate);
280 281
        goto out;
    }
282 283
    av_log(s1, AV_LOG_INFO,
           "device: %s -> display: %s x: %d y: %d width: %d height: %d\n",
284
           s1->filename, param, x_off, y_off, x11grab->width, x11grab->height);
285 286

    dpy = XOpenDisplay(param);
287
    if (!dpy) {
288
        av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
289 290
        ret = AVERROR(EIO);
        goto out;
291
    }
292

293
    st = avformat_new_stream(s1, NULL);
294
    if (!st) {
295 296
        ret = AVERROR(ENOMEM);
        goto out;
297
    }
298
    avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
299

300 301 302 303 304 305 306 307
    screen = DefaultScreen(dpy);

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

        screen_w = DisplayWidth(dpy, screen);
        screen_h = DisplayHeight(dpy, screen);
308 309
        XQueryPointer(dpy, RootWindow(dpy, screen), &w, &w, &x_off, &y_off,
                      &ret, &ret, &ret);
310 311
        x_off -= x11grab->width / 2;
        y_off -= x11grab->height / 2;
312 313 314 315 316
        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);
317 318
    }

319
    use_shm = XShmQueryExtension(dpy);
320
    av_log(s1, AV_LOG_INFO,
321
           "shared memory extension %sfound\n", use_shm ? "" : "not ");
322

323 324 325 326 327 328
    if (use_shm && setup_shm(s1, dpy, &image) < 0) {
        av_log(s1, AV_LOG_WARNING, "Falling back to XGetImage\n");
        use_shm = 0;
    }

    if (!use_shm) {
329
        image = XGetImage(dpy, RootWindow(dpy, screen),
330
                          x_off, y_off,
331
                          x11grab->width, x11grab->height,
332 333 334
                          AllPlanes, ZPixmap);
    }

335 336 337 338 339 340
    if (x11grab->draw_mouse && setup_mouse(dpy, screen) < 0) {
        av_log(s1, AV_LOG_WARNING,
               "XFixes not available, cannot draw the mouse cursor\n");
        x11grab->draw_mouse = 0;
    }

341 342 343
    x11grab->frame_size = x11grab->width * x11grab->height * image->bits_per_pixel / 8;
    x11grab->dpy        = dpy;
    x11grab->time_base  = (AVRational) { framerate.den, framerate.num };
344
    x11grab->time_frame = av_gettime() / av_q2d(x11grab->time_base);
345 346 347 348
    x11grab->x_off      = x_off;
    x11grab->y_off      = y_off;
    x11grab->image      = image;
    x11grab->use_shm    = use_shm;
349

350 351 352 353
    ret = pixfmt_from_image(s1, image, &st->codec->pix_fmt);
    if (ret < 0)
        goto out;

354
    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
355 356 357 358 359
    st->codec->codec_id   = AV_CODEC_ID_RAWVIDEO;
    st->codec->width      = x11grab->width;
    st->codec->height     = x11grab->height;
    st->codec->time_base  = x11grab->time_base;
    st->codec->bit_rate   = x11grab->frame_size * 1 / av_q2d(x11grab->time_base) * 8;
360

361
out:
362
    av_free(param);
363
    return ret;
364 365
}

Edouard Gomez's avatar
Edouard Gomez committed
366
/**
367
 * Paint a mouse pointer in an X11 image.
Edouard Gomez's avatar
Edouard Gomez committed
368
 *
Diego Biurrun's avatar
Diego Biurrun committed
369
 * @param image image to paint the mouse pointer to
Edouard Gomez's avatar
Edouard Gomez committed
370 371 372
 * @param s context used to retrieve original grabbing rectangle
 *          coordinates
 */
373
static void paint_mouse_pointer(XImage *image, X11GrabContext *s)
374
{
375 376 377 378
    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
379 380 381 382 383
    Display *dpy = s->dpy;
    XFixesCursorImage *xcim;
    int x, y;
    int line, column;
    int to_line, to_column;
384 385 386 387 388 389 390 391 392 393
    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;
394

Carl Eugen Hoyos's avatar
Carl Eugen Hoyos committed
395
    xcim = XFixesGetCursorImage(dpy);
396 397
    if (!xcim)
        return;
398

Roxis's avatar
Roxis committed
399 400 401
    x = xcim->x - xcim->xhot;
    y = xcim->y - xcim->yhot;

402 403
    to_line   = FFMIN((y + xcim->height), (height + y_off));
    to_column = FFMIN((x + xcim->width),  (width  + x_off));
Roxis's avatar
Roxis committed
404 405 406

    for (line = FFMAX(y, y_off); line < to_line; line++) {
        for (column = FFMAX(x, x_off); column < to_column; column++) {
407 408 409 410 411 412
            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);
413 414

            if (a == 255) {
415 416 417
                pix[image_addr + 0] = r;
                pix[image_addr + 1] = g;
                pix[image_addr + 2] = b;
418 419
            } else if (a) {
                /* pixel values from XFixesGetCursorImage come premultiplied by alpha */
420 421 422
                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;
423
            }
424 425
        }
    }
Roxis's avatar
Roxis committed
426 427 428

    XFree(xcim);
    xcim = NULL;
429 430
}

Edouard Gomez's avatar
Edouard Gomez committed
431
/**
432
 * Read new data in the image structure.
Edouard Gomez's avatar
Edouard Gomez committed
433 434
 *
 * @param dpy X11 display to grab from
435
 * @param d
Edouard Gomez's avatar
Edouard Gomez committed
436 437 438 439
 * @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
440
 */
441
static int xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
442
{
443 444 445 446
    xGetImageReply rep;
    xGetImageReq *req;
    long nbytes;

447
    if (!image)
Edouard Gomez's avatar
Edouard Gomez committed
448
        return 0;
449 450 451 452 453

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

    /* First set up the standard stuff in the request */
454 455 456 457 458
    req->drawable  = d;
    req->x         = x;
    req->y         = y;
    req->width     = image->width;
    req->height    = image->height;
459
    req->planeMask = (unsigned int)AllPlanes;
460
    req->format    = ZPixmap;
461

Edouard Gomez's avatar
Edouard Gomez committed
462
    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
463 464
        UnlockDisplay(dpy);
        SyncHandle();
Edouard Gomez's avatar
Edouard Gomez committed
465
        return 0;
466 467 468 469 470 471 472
    }

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

    UnlockDisplay(dpy);
    SyncHandle();
Edouard Gomez's avatar
Edouard Gomez committed
473
    return 1;
474 475
}

Edouard Gomez's avatar
Edouard Gomez committed
476
/**
477
 * Grab a frame from x11 (public device demuxer API).
Edouard Gomez's avatar
Edouard Gomez committed
478 479 480 481 482
 *
 * @param s1 Context from avformat core
 * @param pkt Packet holding the brabbed frame
 * @return frame size in bytes
 */
483
static int x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
484
{
485
    X11GrabContext *s = s1->priv_data;
486 487 488 489 490
    Display *dpy      = s->dpy;
    XImage *image     = s->image;
    int x_off         = s->x_off;
    int y_off         = s->y_off;
    int follow_mouse  = s->follow_mouse;
491 492
    int screen, pointer_x, pointer_y, _, same_screen = 1;
    Window w, root;
493 494 495 496
    int64_t curtime, delay;
    struct timespec ts;

    /* Calculate the time of the next frame */
497
    s->time_frame += INT64_C(1000000);
498 499

    /* wait based on the frame rate */
500
    for (;;) {
501
        curtime = av_gettime();
502
        delay   = s->time_frame * av_q2d(s->time_base) - curtime;
503
        if (delay <= 0) {
504
            if (delay < INT64_C(-1000000) * av_q2d(s->time_base))
505
                s->time_frame += INT64_C(1000000);
506 507
            break;
        }
508
        ts.tv_sec  = delay / 1000000;
509 510 511 512
        ts.tv_nsec = (delay % 1000000) * 1000;
        nanosleep(&ts, NULL);
    }

513 514 515
    av_init_packet(pkt);
    pkt->data = image->data;
    pkt->size = s->frame_size;
516
    pkt->pts  = curtime;
517

518
    screen = DefaultScreen(dpy);
519
    root   = RootWindow(dpy, screen);
520 521 522 523 524 525

    if (follow_mouse || s->draw_mouse)
        same_screen = XQueryPointer(dpy, root, &w, &w,
                                    &pointer_x, &pointer_y, &_, &_, &_);

    if (follow_mouse && same_screen) {
526 527 528 529 530 531
        int screen_w, screen_h;

        screen_w = DisplayWidth(dpy, screen);
        screen_h = DisplayHeight(dpy, screen);
        if (follow_mouse == -1) {
            // follow the mouse, put it at center of grabbing region
532
            x_off += pointer_x - s->width / 2 - x_off;
533 534 535 536
            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.
537
            if (pointer_x > x_off + s->width - follow_mouse)
538
                x_off += pointer_x - (x_off + s->width - follow_mouse);
539
            else if (pointer_x < x_off + follow_mouse)
540
                x_off -= (x_off + follow_mouse) - pointer_x;
541
            if (pointer_y > y_off + s->height - follow_mouse)
542
                y_off += pointer_y - (y_off + s->height - follow_mouse);
543
            else if (pointer_y < y_off + follow_mouse)
544 545 546 547 548
                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);
549 550 551 552 553 554 555

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

556
    if (s->show_region && same_screen) {
557
        if (s->region_win) {
558 559 560 561
            XEvent evt = { .type = NoEventMask };
            // Clean up the events, and do the initial draw or redraw.
            while (XCheckMaskEvent(dpy, ExposureMask | StructureNotifyMask,
                                   &evt))
562
                ;
563 564 565 566 567
            if (evt.type)
                x11grab_draw_region_win(s);
        } else {
            x11grab_region_win_init(s);
        }
568 569
    }

570 571 572
    if (s->use_shm) {
        if (!XShmGetImage(dpy, root, image, x_off, y_off, AllPlanes))
            av_log(s1, AV_LOG_INFO, "XShmGetImage() failed\n");
573
    } else {
574 575
        if (!xget_zpixmap(dpy, root, image, x_off, y_off))
            av_log(s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
576 577
    }

578
    if (s->draw_mouse && same_screen)
Roxis's avatar
Roxis committed
579
        paint_mouse_pointer(image, s);
580 581

    return s->frame_size;
582 583
}

Edouard Gomez's avatar
Edouard Gomez committed
584
/**
585
 * Close x11 frame grabber (public device demuxer API).
Edouard Gomez's avatar
Edouard Gomez committed
586 587 588 589
 *
 * @param s1 Context from avformat core
 * @return 0 success, !0 failure
 */
590
static int x11grab_read_close(AVFormatContext *s1)
591
{
592
    X11GrabContext *x11grab = s1->priv_data;
593 594 595 596 597 598 599 600 601 602 603 604 605 606

    /* 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;
    }

607
    if (x11grab->region_win)
608 609
        XDestroyWindow(x11grab->dpy, x11grab->region_win);

610 611 612
    /* Free X11 display */
    XCloseDisplay(x11grab->dpy);
    return 0;
613 614
}

615
#define OFFSET(x) offsetof(X11GrabContext, x)
616 617
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
Christian Hujer's avatar
Christian Hujer committed
618 619
    { "grab_x", "Initial x coordinate.", OFFSET(x_off), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, DEC },
    { "grab_y", "Initial y coordinate.", OFFSET(y_off), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, DEC },
620 621
    { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = "vga"}, 0, 0, DEC },
    { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "ntsc"}, 0, 0, DEC },
622
    { "draw_mouse", "Draw the mouse pointer.", OFFSET(draw_mouse), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, DEC },
623
    { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
624
      OFFSET(follow_mouse), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT_MAX, DEC, "follow_mouse" },
625
    { "centered", "Keep the mouse pointer at the center of grabbing region when following.", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, DEC, "follow_mouse" },
626
    { "show_region", "Show the grabbing region.", OFFSET(show_region), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, DEC },
627 628 629 630 631 632 633 634 635 636
    { NULL },
};

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

Edouard Gomez's avatar
Edouard Gomez committed
637
/** x11 grabber device demuxer declaration */
638
AVInputFormat ff_x11grab_demuxer = {
639 640
    .name           = "x11grab",
    .long_name      = NULL_IF_CONFIG_SMALL("X11grab"),
641
    .priv_data_size = sizeof(X11GrabContext),
642 643 644 645 646
    .read_header    = x11grab_read_header,
    .read_packet    = x11grab_read_packet,
    .read_close     = x11grab_read_close,
    .flags          = AVFMT_NOFILE,
    .priv_class     = &x11_class,
647
};