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

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
#include "libavformat/avformat.h"
41
#include "libavformat/internal.h"
42 43 44
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
45 46 47 48 49
#include <time.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#include <X11/Xproto.h>
50
#include <X11/Xutil.h>
51
#include <sys/shm.h>
52
#include <X11/extensions/shape.h>
53
#include <X11/extensions/XShm.h>
Roxis's avatar
Roxis committed
54
#include <X11/extensions/Xfixes.h>
55

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

66
    char *video_size;        /**< String describing video size, set by a private option. */
Edouard Gomez's avatar
Edouard Gomez committed
67 68 69 70 71 72 73 74 75
    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 */
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
    char *framerate;         /**< Set by a private option. */
80 81

    Window region_win;       /**< This is used by show_region option. */
82
};
Edouard Gomez's avatar
Edouard Gomez committed
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 143 144
#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);
}

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

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

180 181 182 183
    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;
    }
184 185 186 187
    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;
    }
188 189
    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);
190 191 192 193

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

198
    st = avformat_new_stream(s1, NULL);
199
    if (!st) {
200 201
        ret = AVERROR(ENOMEM);
        goto out;
202
    }
203
    avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
204

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
    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);
    }

221
    use_shm = XShmQueryExtension(dpy);
Edouard Gomez's avatar
Edouard Gomez committed
222
    av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
223 224 225 226 227 228 229 230 231

    if(use_shm) {
        int scr = XDefaultScreen(dpy);
        image = XShmCreateImage(dpy,
                                DefaultVisual(dpy, scr),
                                DefaultDepth(dpy, scr),
                                ZPixmap,
                                NULL,
                                &x11grab->shminfo,
232
                                x11grab->width, x11grab->height);
233 234 235 236 237
        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");
238 239
            ret = AVERROR(ENOMEM);
            goto out;
240 241 242 243 244 245 246
        }
        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 :) */
247 248
            ret = AVERROR(EIO);
            goto out;
249 250
        }
    } else {
251
        image = XGetImage(dpy, RootWindow(dpy, screen),
252
                          x_off,y_off,
253
                          x11grab->width, x11grab->height,
254 255 256 257 258
                          AllPlanes, ZPixmap);
    }

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

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

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

322 323
out:
    return ret;
324 325
}

Edouard Gomez's avatar
Edouard Gomez committed
326
/**
327
 * Paint a mouse pointer in an X11 image.
Edouard Gomez's avatar
Edouard Gomez committed
328
 *
Diego Biurrun's avatar
Diego Biurrun committed
329
 * @param image image to paint the mouse pointer to
Edouard Gomez's avatar
Edouard Gomez committed
330 331 332
 * @param s context used to retrieve original grabbing rectangle
 *          coordinates
 */
333
static void
Roxis's avatar
Roxis committed
334
paint_mouse_pointer(XImage *image, struct x11_grab *s)
335
{
336 337 338 339
    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
340 341 342 343 344
    Display *dpy = s->dpy;
    XFixesCursorImage *xcim;
    int x, y;
    int line, column;
    int to_line, to_column;
345 346 347 348 349 350 351 352 353 354
    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;
355

Carl Eugen Hoyos's avatar
Carl Eugen Hoyos committed
356
    xcim = XFixesGetCursorImage(dpy);
357

Roxis's avatar
Roxis committed
358 359 360 361 362 363 364 365
    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++) {
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
            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;
382
            }
383 384
        }
    }
Roxis's avatar
Roxis committed
385 386 387

    XFree(xcim);
    xcim = NULL;
388 389 390
}


Edouard Gomez's avatar
Edouard Gomez committed
391
/**
392
 * Read new data in the image structure.
Edouard Gomez's avatar
Edouard Gomez committed
393 394
 *
 * @param dpy X11 display to grab from
395
 * @param d
Edouard Gomez's avatar
Edouard Gomez committed
396 397 398 399
 * @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
400 401
 */
static int
Edouard Gomez's avatar
Edouard Gomez committed
402
xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
403
{
404 405 406 407 408
    xGetImageReply rep;
    xGetImageReq *req;
    long nbytes;

    if (!image) {
Edouard Gomez's avatar
Edouard Gomez committed
409
        return 0;
410 411 412 413 414 415 416 417 418 419 420 421 422 423
    }

    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;

Edouard Gomez's avatar
Edouard Gomez committed
424
    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
425 426
        UnlockDisplay(dpy);
        SyncHandle();
Edouard Gomez's avatar
Edouard Gomez committed
427
        return 0;
428 429 430 431 432 433 434
    }

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

    UnlockDisplay(dpy);
    SyncHandle();
Edouard Gomez's avatar
Edouard Gomez committed
435
    return 1;
436 437
}

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

454 455 456 457
    int screen;
    Window root;
    int follow_mouse = s->follow_mouse;

458 459 460 461
    int64_t curtime, delay;
    struct timespec ts;

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

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

479 480 481
    av_init_packet(pkt);
    pkt->data = image->data;
    pkt->size = s->frame_size;
Edouard Gomez's avatar
Edouard Gomez committed
482
    pkt->pts = curtime;
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 511 512
    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);
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529

        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);
        }
530 531
    }

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

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

    return s->frame_size;
547 548
}

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

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

573 574 575 576
    if (x11grab->region_win) {
        XDestroyWindow(x11grab->dpy, x11grab->region_win);
    }

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

582 583 584
#define OFFSET(x) offsetof(struct x11_grab, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
585 586 587
    { "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 },
    { "draw_mouse", "Draw the mouse pointer.", OFFSET(draw_mouse), AV_OPT_TYPE_INT, { 1 }, 0, 1, DEC },
588
    { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
589 590 591
      OFFSET(follow_mouse), AV_OPT_TYPE_INT, { 0 }, -1, INT_MAX, DEC, "follow_mouse" },
    { "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" },
    { "show_region", "Show the grabbing region.", OFFSET(show_region), AV_OPT_TYPE_INT, { 0 }, 0, 1, DEC },
592 593 594 595 596 597 598 599 600 601
    { 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
602
/** x11 grabber device demuxer declaration */
603 604 605 606 607 608 609 610 611
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,
612
};