Browse code

xcbgrab: XCB-based screen capture

Matches the x11grab screen capture by features.

Luca Barbato authored on 2014/08/24 21:18:22
Showing 6 changed files
... ...
@@ -5,6 +5,7 @@ version <next>:
5 5
 - aliases and defaults for Ogg subtypes (opus, spx)
6 6
 - HEVC/H.265 RTP payload format (draft v6) packetizer and depacketizer
7 7
 - avplay now exits by default at the end of playback
8
+- XCB-based screen-grabber
8 9
 
9 10
 
10 11
 version 11:
... ...
@@ -210,10 +210,13 @@ External library support:
210 210
   --enable-libx264         enable H.264 encoding via x264 [no]
211 211
   --enable-libx265         enable HEVC encoding via x265 [no]
212 212
   --enable-libxavs         enable AVS encoding via xavs [no]
213
+  --enable-libxcb          enable X11 grabbing using XCB [no]
214
+  --enable-libxcb-shm      enable X11 grabbing shm communication [auto]
215
+  --enable-libxcb-xfixes   enable X11 grabbing mouse rendering [auto]
213 216
   --enable-libxvid         enable Xvid encoding via xvidcore,
214 217
                            native MPEG-4/Xvid encoder exists [no]
215 218
   --enable-openssl         enable openssl [no]
216
-  --enable-x11grab         enable X11 grabbing [no]
219
+  --enable-x11grab         enable X11 grabbing (legacy) [no]
217 220
   --enable-zlib            enable zlib [autodetect]
218 221
 
219 222
 Toolchain options:
... ...
@@ -1170,6 +1173,9 @@ EXTERNAL_LIBRARY_LIST="
1170 1170
     libx264
1171 1171
     libx265
1172 1172
     libxavs
1173
+    libxcb
1174
+    libxcb_shm
1175
+    libxcb_xfixes
1173 1176
     libxvid
1174 1177
     openssl
1175 1178
     x11grab
... ...
@@ -2102,7 +2108,8 @@ sndio_outdev_deps="sndio_h"
2102 2102
 v4l2_indev_deps_any="linux_videodev2_h sys_videoio_h"
2103 2103
 vfwcap_indev_deps="capCreateCaptureWindow vfwcap_defines"
2104 2104
 vfwcap_indev_extralibs="-lavicap32"
2105
-x11grab_indev_deps="x11grab XShmCreateImage"
2105
+x11grab_indev_deps="x11grab"
2106
+x11grab_xcb_indev_deps="libxcb"
2106 2107
 
2107 2108
 # protocols
2108 2109
 ffrtmpcrypt_protocol_deps="!librtmp_protocol"
... ...
@@ -4273,10 +4280,30 @@ fi
4273 4273
 
4274 4274
 check_lib X11/Xlib.h XOpenDisplay -lX11 && enable xlib
4275 4275
 
4276
-enabled x11grab                                           &&
4277
-require Xext X11/extensions/XShm.h XShmCreateImage -lXext &&
4278
-require Xfixes X11/extensions/Xfixes.h XFixesGetCursorImage -lXfixes &&
4279
-{ enabled xlib || die "ERROR: Xlib not found"; }
4276
+if enabled libxcb || enabled x11grab && ! disabled libxcb; then
4277
+    check_pkg_config xcb-event xcb/xcb.h xcb_connect || {
4278
+        enabled libxcb && die "ERROR: libxcb not found";
4279
+    } && disable x11grab && enable libxcb
4280
+
4281
+    disabled libxcb_shm ||
4282
+        check_pkg_config xcb-shm xcb/shm.h xcb_shm_attach || {
4283
+            enabled libxcb_shm && die "ERROR: libxcb_shm not found";
4284
+        } && check_header sys/shm.h && enable libxcb_shm
4285
+
4286
+    disabled libxcb_xfixes ||
4287
+        check_pkg_config xcb-xfixes xcb/xfixes.h xcb_xfixes_get_cursor_image || {
4288
+            enabled libxcb_xfixes && die "ERROR: libxcb_xfixes not found";
4289
+        } && enable libxcb_xfixes
4290
+
4291
+    add_cflags "$xcb_event_cflags $xcb_shm_cflags $xcb_xfixes_cflags"
4292
+    add_extralibs "$xcb_event_libs $xcb_shm_libs $xcb_xfixes_libs"
4293
+fi
4294
+
4295
+if enabled x11grab; then
4296
+    enabled xlib || die "ERROR: Xlib not found"
4297
+    require Xext X11/extensions/XShm.h XShmCreateImage -lXext
4298
+    require Xfixes X11/extensions/Xfixes.h XFixesGetCursorImage -lXfixes
4299
+fi
4280 4300
 
4281 4301
 enabled vdpau &&
4282 4302
     check_cpp_condition vdpau/vdpau.h "defined VDP_DECODER_PROFILE_MPEG4_PART2_ASP" ||
... ...
@@ -22,7 +22,8 @@ OBJS-$(CONFIG_SNDIO_INDEV)               += sndio_common.o sndio_dec.o
22 22
 OBJS-$(CONFIG_SNDIO_OUTDEV)              += sndio_common.o sndio_enc.o
23 23
 OBJS-$(CONFIG_V4L2_INDEV)                += v4l2.o
24 24
 OBJS-$(CONFIG_VFWCAP_INDEV)              += vfwcap.o
25
-OBJS-$(CONFIG_X11GRAB_INDEV)             += x11grab.o
25
+OBJS-$(CONFIG_X11GRAB_XLIB_INDEV)        += x11grab.o
26
+OBJS-$(CONFIG_X11GRAB_XCB_INDEV)         += xcbgrab.o
26 27
 
27 28
 # external libraries
28 29
 OBJS-$(CONFIG_LIBCDIO_INDEV)             += libcdio.o
... ...
@@ -58,6 +58,7 @@ void avdevice_register_all(void)
58 58
     REGISTER_INDEV   (V4L2,             v4l2);
59 59
     REGISTER_INDEV   (VFWCAP,           vfwcap);
60 60
     REGISTER_INDEV   (X11GRAB,          x11grab);
61
+    REGISTER_INDEV   (X11GRAB_XCB,      x11grab_xcb);
61 62
 
62 63
     /* external libraries */
63 64
     REGISTER_INDEV   (LIBCDIO,          libcdio);
... ...
@@ -28,7 +28,7 @@
28 28
 #include "libavutil/version.h"
29 29
 
30 30
 #define LIBAVDEVICE_VERSION_MAJOR 55
31
-#define LIBAVDEVICE_VERSION_MINOR  0
31
+#define LIBAVDEVICE_VERSION_MINOR  1
32 32
 #define LIBAVDEVICE_VERSION_MICRO  0
33 33
 
34 34
 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
35 35
new file mode 100644
... ...
@@ -0,0 +1,655 @@
0
+/*
1
+ * XCB input grabber
2
+ * Copyright (C) 2014 Luca Barbato <lu_zero@gentoo.org>
3
+ *
4
+ * This file is part of Libav.
5
+ *
6
+ * Libav is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * Libav is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with Libav; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+#include "config.h"
22
+
23
+#include <stdlib.h>
24
+#include <xcb/xcb.h>
25
+
26
+#if CONFIG_LIBXCB_XFIXES
27
+#include <xcb/xfixes.h>
28
+#endif
29
+
30
+#if CONFIG_LIBXCB_SHM
31
+#include <sys/shm.h>
32
+#include <xcb/shm.h>
33
+#endif
34
+
35
+#include "libavformat/avformat.h"
36
+#include "libavformat/internal.h"
37
+
38
+#include "libavutil/mathematics.h"
39
+#include "libavutil/opt.h"
40
+#include "libavutil/parseutils.h"
41
+#include "libavutil/time.h"
42
+
43
+typedef struct XCBGrabContext {
44
+    const AVClass *class;
45
+
46
+    xcb_connection_t *conn;
47
+    xcb_screen_t *screen;
48
+    xcb_window_t window;
49
+    xcb_shm_seg_t segment;
50
+
51
+    int64_t time_frame;
52
+    AVRational time_base;
53
+
54
+    int x, y;
55
+    int width, height;
56
+    int frame_size;
57
+    int bpp;
58
+
59
+    int draw_mouse;
60
+    int follow_mouse;
61
+    int show_region;
62
+    int region_border;
63
+    int centered;
64
+
65
+    const char *video_size;
66
+    const char *framerate;
67
+
68
+    int has_shm;
69
+} XCBGrabContext;
70
+
71
+#define FOLLOW_CENTER -1
72
+
73
+#define OFFSET(x) offsetof(XCBGrabContext, x)
74
+#define D AV_OPT_FLAG_DECODING_PARAM
75
+static const AVOption options[] = {
76
+    { "x", "Initial x coordinate.", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D },
77
+    { "y", "Initial y coordinate.", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D },
78
+    { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = "vga" }, 0, 0, D },
79
+    { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "ntsc" }, 0, 0, D },
80
+    { "draw_mouse", "Draw the mouse pointer.", OFFSET(draw_mouse), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, D },
81
+    { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
82
+      OFFSET(follow_mouse), AV_OPT_TYPE_INT, { .i64 = 0 },  FOLLOW_CENTER, INT_MAX, D, "follow_mouse" },
83
+    { "centered", "Keep the mouse pointer at the center of grabbing region when following.", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, D, "follow_mouse" },
84
+    { "show_region", "Show the grabbing region.", OFFSET(show_region), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D },
85
+    { "region_border", "Set the region border thickness.", OFFSET(region_border), AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 128, D },
86
+    { NULL },
87
+};
88
+
89
+static const AVClass xcbgrab_class = {
90
+    .class_name = "xcbgrab indev",
91
+    .item_name  = av_default_item_name,
92
+    .option     = options,
93
+    .version    = LIBAVUTIL_VERSION_INT,
94
+};
95
+
96
+static int xcbgrab_reposition(AVFormatContext *s,
97
+                              xcb_query_pointer_reply_t *p,
98
+                              xcb_get_geometry_reply_t *geo)
99
+{
100
+    XCBGrabContext *c = s->priv_data;
101
+    int x = c->x, y = c->y, p_x = p->win_x, p_y = p->win_y;
102
+    int w = c->width, h = c->height, f = c->follow_mouse;
103
+
104
+    if (!p || !geo)
105
+        return AVERROR(EIO);
106
+
107
+    if (f == FOLLOW_CENTER) {
108
+        x = p_x - w / 2;
109
+        y = p_y - h / 2;
110
+    } else {
111
+        int left   = x + f;
112
+        int right  = x + w - f;
113
+        int top    = y + f;
114
+        int bottom = y + h + f;
115
+        if (p_x > right) {
116
+            x += p_x - right;
117
+        } else if (p_x < left) {
118
+            x -= left - p_x;
119
+        }
120
+        if (p_y > bottom) {
121
+            y += p_y - bottom;
122
+        } else if (p_y < top) {
123
+            y -= top - p_y;
124
+        }
125
+    }
126
+
127
+    c->x = FFMIN(FFMAX(0, x), geo->width  - w);
128
+    c->y = FFMIN(FFMAX(0, y), geo->height - h);
129
+
130
+    return 0;
131
+}
132
+
133
+static int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt)
134
+{
135
+    XCBGrabContext *c = s->priv_data;
136
+    xcb_get_image_cookie_t iq;
137
+    xcb_get_image_reply_t *img;
138
+    xcb_drawable_t drawable = c->screen->root;
139
+    uint8_t *data;
140
+    int length, ret;
141
+
142
+    iq  = xcb_get_image(c->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable,
143
+                        c->x, c->y, c->width, c->height, ~0);
144
+
145
+    img = xcb_get_image_reply(c->conn, iq, NULL);
146
+    if (!img)
147
+        return AVERROR(EAGAIN);
148
+
149
+    data   = xcb_get_image_data(img);
150
+    length = xcb_get_image_data_length(img);
151
+
152
+    ret = av_new_packet(pkt, length);
153
+
154
+    if (!ret)
155
+        memcpy(pkt->data, data, length);
156
+
157
+    free(img);
158
+
159
+    return ret;
160
+}
161
+
162
+static void wait_frame(AVFormatContext *s, AVPacket *pkt)
163
+{
164
+    XCBGrabContext *c = s->priv_data;
165
+    int64_t curtime, delay;
166
+    int64_t frame_time = av_rescale_q(1, c->time_base, AV_TIME_BASE_Q);
167
+
168
+    c->time_frame += frame_time;
169
+
170
+    for (;;) {
171
+        curtime = av_gettime();
172
+        delay   = c->time_frame - curtime;
173
+        if (delay <= 0)
174
+            break;
175
+        av_usleep(delay);
176
+    }
177
+
178
+    pkt->pts = curtime;
179
+}
180
+
181
+#if CONFIG_LIBXCB_SHM
182
+static int check_shm(xcb_connection_t *conn)
183
+{
184
+    xcb_shm_query_version_cookie_t cookie = xcb_shm_query_version(conn);
185
+    xcb_shm_query_version_reply_t *reply;
186
+
187
+    reply = xcb_shm_query_version_reply(conn, cookie, NULL);
188
+    if (reply) {
189
+        free(reply);
190
+        return 1;
191
+    }
192
+
193
+    return 0;
194
+}
195
+
196
+static void dealloc_shm(void *unused, uint8_t *data)
197
+{
198
+    shmdt(data);
199
+}
200
+
201
+static int xcbgrab_frame_shm(AVFormatContext *s, AVPacket *pkt)
202
+{
203
+    XCBGrabContext *c = s->priv_data;
204
+    xcb_shm_get_image_cookie_t iq;
205
+    xcb_shm_get_image_reply_t *img;
206
+    xcb_drawable_t drawable = c->screen->root;
207
+    uint8_t *data;
208
+    int size = c->frame_size + FF_INPUT_BUFFER_PADDING_SIZE;
209
+    int id   = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
210
+    xcb_generic_error_t *e = NULL;
211
+
212
+    if (id == -1) {
213
+        char errbuf[1024];
214
+        int err = AVERROR(errno);
215
+        av_strerror(err, errbuf, sizeof(errbuf));
216
+        av_log(s, AV_LOG_ERROR, "Cannot get %d bytes of shared memory: %s.\n",
217
+               size, errbuf);
218
+        return err;
219
+    }
220
+
221
+    xcb_shm_attach(c->conn, c->segment, id, 0);
222
+
223
+    iq = xcb_shm_get_image(c->conn, drawable,
224
+                           c->x, c->y, c->width, c->height, ~0,
225
+                           XCB_IMAGE_FORMAT_Z_PIXMAP, c->segment, 0);
226
+
227
+    xcb_shm_detach(c->conn, c->segment);
228
+
229
+    img = xcb_shm_get_image_reply(c->conn, iq, &e);
230
+
231
+    xcb_flush(c->conn);
232
+
233
+    if (e) {
234
+        av_log(s, AV_LOG_ERROR,
235
+               "Cannot get the image data "
236
+               "event_error: response_type:%u error_code:%u "
237
+               "sequence:%u resource_id:%u minor_code:%u major_code:%u.\n",
238
+               e->response_type, e->error_code,
239
+               e->sequence, e->resource_id, e->minor_code, e->major_code);
240
+
241
+        shmctl(id, IPC_RMID, 0);
242
+        return AVERROR(EACCES);
243
+    }
244
+
245
+    free(img);
246
+
247
+    data = shmat(id, NULL, 0);
248
+    shmctl(id, IPC_RMID, 0);
249
+
250
+    if ((intptr_t)data == -1)
251
+        return AVERROR(errno);
252
+
253
+    pkt->buf = av_buffer_create(data, size, dealloc_shm, NULL, 0);
254
+    if (!pkt->buf) {
255
+        shmdt(data);
256
+        return AVERROR(ENOMEM);
257
+    }
258
+
259
+    pkt->data = pkt->buf->data;
260
+    pkt->size = c->frame_size;
261
+
262
+    return 0;
263
+}
264
+#endif /* CONFIG_LIBXCB_SHM */
265
+
266
+#if CONFIG_LIBXCB_XFIXES
267
+static int check_xfixes(xcb_connection_t *conn)
268
+{
269
+    xcb_xfixes_query_version_cookie_t cookie;
270
+    xcb_xfixes_query_version_reply_t *reply;
271
+
272
+    cookie = xcb_xfixes_query_version(conn, XCB_XFIXES_MAJOR_VERSION,
273
+                                      XCB_XFIXES_MINOR_VERSION);
274
+    reply  = xcb_xfixes_query_version_reply(conn, cookie, NULL);
275
+
276
+    if (reply) {
277
+        free(reply);
278
+        return 1;
279
+    }
280
+    return 0;
281
+}
282
+
283
+#define BLEND(target, source, alpha) \
284
+    (target) + ((source) * (255 - (alpha)) + 255 / 2) / 255
285
+
286
+static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt,
287
+                               xcb_query_pointer_reply_t *p,
288
+                               xcb_get_geometry_reply_t *geo)
289
+{
290
+    XCBGrabContext *gr = s->priv_data;
291
+    uint32_t *cursor;
292
+    uint8_t *image = pkt->data;
293
+    int stride     = gr->bpp / 8;
294
+    xcb_xfixes_get_cursor_image_cookie_t cc;
295
+    xcb_xfixes_get_cursor_image_reply_t *ci;
296
+    int cx, cy, x, y, w, h, c_off, i_off;
297
+
298
+    cc = xcb_xfixes_get_cursor_image(gr->conn);
299
+    ci = xcb_xfixes_get_cursor_image_reply(gr->conn, cc, NULL);
300
+    if (!ci)
301
+        return;
302
+
303
+    cursor = xcb_xfixes_get_cursor_image_cursor_image(ci);
304
+    if (!cursor)
305
+        return;
306
+
307
+    cx = ci->x - ci->xhot;
308
+    cy = ci->y - ci->yhot;
309
+
310
+    x = FFMAX(cx, gr->x);
311
+    y = FFMAX(cy, gr->y);
312
+
313
+    w = FFMIN(cx + ci->width,  gr->x + gr->width)  - x;
314
+    h = FFMIN(cy + ci->height, gr->y + gr->height) - y;
315
+
316
+    c_off = x - cx;
317
+    i_off = x - gr->x;
318
+
319
+    cursor += (y - cy) * ci->width;
320
+    image  += (y - gr->y) * gr->width * stride;
321
+
322
+    for (y = 0; y < h; y++) {
323
+        cursor += c_off;
324
+        image  += i_off * stride;
325
+        for (x = 0; x < w; x++, cursor++, image += stride) {
326
+            int r, g, b, a;
327
+
328
+            r =  *cursor        & 0xff;
329
+            g = (*cursor >>  8) & 0xff;
330
+            b = (*cursor >> 16) & 0xff;
331
+            a = (*cursor >> 24) & 0xff;
332
+
333
+            if (!a)
334
+                continue;
335
+
336
+            if (a == 255) {
337
+                image[0] = r;
338
+                image[1] = g;
339
+                image[2] = b;
340
+            } else {
341
+                image[0] = BLEND(r, image[0], a);
342
+                image[1] = BLEND(g, image[1], a);
343
+                image[2] = BLEND(b, image[2], a);
344
+            }
345
+
346
+        }
347
+        cursor +=  ci->width - w - c_off;
348
+        image  += (gr->width - w - i_off) * stride;
349
+    }
350
+
351
+    free(ci);
352
+}
353
+#endif /* CONFIG_LIBXCB_XFIXES */
354
+
355
+static void xcbgrab_update_region(AVFormatContext *s)
356
+{
357
+    XCBGrabContext *c     = s->priv_data;
358
+    const uint32_t args[] = { c->x - c->region_border,
359
+                              c->y - c->region_border };
360
+
361
+    xcb_configure_window(c->conn,
362
+                         c->window,
363
+                         XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
364
+                         args);
365
+}
366
+
367
+static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt)
368
+{
369
+    XCBGrabContext *c = s->priv_data;
370
+    xcb_query_pointer_cookie_t pc;
371
+    xcb_get_geometry_cookie_t gc;
372
+    xcb_query_pointer_reply_t *p  = NULL;
373
+    xcb_get_geometry_reply_t *geo = NULL;
374
+    int ret = 0;
375
+
376
+    wait_frame(s, pkt);
377
+
378
+    if (c->follow_mouse || c->draw_mouse) {
379
+        pc  = xcb_query_pointer(c->conn, c->screen->root);
380
+        gc  = xcb_get_geometry(c->conn, c->screen->root);
381
+        p   = xcb_query_pointer_reply(c->conn, pc, NULL);
382
+        geo = xcb_get_geometry_reply(c->conn, gc, NULL);
383
+    }
384
+
385
+    if (c->follow_mouse && p->same_screen)
386
+        xcbgrab_reposition(s, p, geo);
387
+
388
+    if (c->show_region)
389
+        xcbgrab_update_region(s);
390
+
391
+#if CONFIG_LIBXCB_SHM
392
+    if (c->has_shm && xcbgrab_frame_shm(s, pkt) < 0)
393
+        c->has_shm = 0;
394
+#endif
395
+    if (!c->has_shm)
396
+        ret = xcbgrab_frame(s, pkt);
397
+
398
+#if CONFIG_LIBXCB_XFIXES
399
+    if (c->draw_mouse && p->same_screen)
400
+        xcbgrab_draw_mouse(s, pkt, p, geo);
401
+#endif
402
+
403
+    free(p);
404
+    free(geo);
405
+
406
+    return ret;
407
+}
408
+
409
+static av_cold int xcbgrab_read_close(AVFormatContext *s)
410
+{
411
+    XCBGrabContext *ctx = s->priv_data;
412
+
413
+    xcb_disconnect(ctx->conn);
414
+
415
+    return 0;
416
+}
417
+
418
+static xcb_screen_t *get_screen(const xcb_setup_t *setup, int screen_num)
419
+{
420
+    xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup);
421
+    xcb_screen_t *screen     = NULL;
422
+
423
+    for (; it.rem > 0; xcb_screen_next (&it)) {
424
+        if (!screen_num) {
425
+            screen = it.data;
426
+            break;
427
+        }
428
+
429
+        screen_num--;
430
+    }
431
+
432
+    return screen;
433
+}
434
+
435
+static int pixfmt_from_pixmap_format(AVFormatContext *s, int depth,
436
+                                     int *pix_fmt)
437
+{
438
+    XCBGrabContext *c        = s->priv_data;
439
+    const xcb_setup_t *setup = xcb_get_setup(c->conn);
440
+    const xcb_format_t *fmt  = xcb_setup_pixmap_formats(setup);
441
+    int length               = xcb_setup_pixmap_formats_length(setup);
442
+
443
+    *pix_fmt = 0;
444
+
445
+    while (length--) {
446
+        if (fmt->depth == depth) {
447
+            switch (depth) {
448
+            case 32:
449
+                if (fmt->bits_per_pixel == 32)
450
+                    *pix_fmt = AV_PIX_FMT_ARGB;
451
+                break;
452
+            case 24:
453
+                if (fmt->bits_per_pixel == 32)
454
+                    *pix_fmt = AV_PIX_FMT_RGB32;
455
+                else if (fmt->bits_per_pixel == 24)
456
+                    *pix_fmt = AV_PIX_FMT_RGB24;
457
+                break;
458
+            case 16:
459
+                if (fmt->bits_per_pixel == 16)
460
+                    *pix_fmt = AV_PIX_FMT_RGB565;
461
+                break;
462
+            case 15:
463
+                if (fmt->bits_per_pixel == 16)
464
+                    *pix_fmt = AV_PIX_FMT_RGB555;
465
+                break;
466
+            case 8:
467
+                if (fmt->bits_per_pixel == 8)
468
+                    *pix_fmt = AV_PIX_FMT_RGB8;
469
+                break;
470
+            }
471
+        }
472
+
473
+        if (*pix_fmt) {
474
+            c->bpp        = fmt->bits_per_pixel;
475
+            c->frame_size = c->width * c->height * fmt->bits_per_pixel / 8;
476
+            return 0;
477
+        }
478
+
479
+        fmt++;
480
+    }
481
+    av_log(s, AV_LOG_ERROR, "Pixmap format not mappable.\n");
482
+
483
+    return AVERROR_PATCHWELCOME;
484
+}
485
+
486
+static int create_stream(AVFormatContext *s)
487
+{
488
+    XCBGrabContext *c = s->priv_data;
489
+    AVStream *st      = avformat_new_stream(s, NULL);
490
+    const char *opts  = strchr(s->filename, '+');
491
+    xcb_get_geometry_cookie_t gc;
492
+    xcb_get_geometry_reply_t *geo;
493
+    int ret;
494
+
495
+    if (!st)
496
+        return AVERROR(ENOMEM);
497
+
498
+    ret = av_parse_video_size(&c->width, &c->height, c->video_size);
499
+    if (ret < 0)
500
+        return ret;
501
+
502
+    ret = av_parse_video_rate(&st->avg_frame_rate, c->framerate);
503
+    if (ret < 0)
504
+        return ret;
505
+
506
+    if (opts)
507
+        sscanf(opts, "%d,%d", &c->x, &c->y);
508
+
509
+    avpriv_set_pts_info(st, 64, 1, 1000000);
510
+
511
+    gc  = xcb_get_geometry(c->conn, c->screen->root);
512
+    geo = xcb_get_geometry_reply(c->conn, gc, NULL);
513
+
514
+    c->width      = FFMIN(geo->width, c->width);
515
+    c->height     = FFMIN(geo->height, c->height);
516
+    c->time_base  = (AVRational){ st->avg_frame_rate.den,
517
+                                  st->avg_frame_rate.num };
518
+    c->time_frame = av_gettime();
519
+
520
+    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
521
+    st->codec->codec_id   = AV_CODEC_ID_RAWVIDEO;
522
+    st->codec->width      = c->width;
523
+    st->codec->height     = c->height;
524
+    st->codec->time_base  = c->time_base;
525
+
526
+    ret = pixfmt_from_pixmap_format(s, geo->depth, &st->codec->pix_fmt);
527
+
528
+    free(geo);
529
+
530
+    return ret;
531
+}
532
+
533
+static void draw_rectangle(AVFormatContext *s)
534
+{
535
+    XCBGrabContext *c = s->priv_data;
536
+    xcb_gcontext_t gc = xcb_generate_id(c->conn);
537
+    uint32_t mask     = XCB_GC_FOREGROUND |
538
+                        XCB_GC_BACKGROUND |
539
+                        XCB_GC_LINE_WIDTH |
540
+                        XCB_GC_LINE_STYLE |
541
+                        XCB_GC_FILL_STYLE;
542
+    uint32_t values[] = { c->screen->black_pixel,
543
+                          c->screen->white_pixel,
544
+                          c->region_border,
545
+                          XCB_LINE_STYLE_DOUBLE_DASH,
546
+                          XCB_FILL_STYLE_SOLID };
547
+    xcb_rectangle_t r = { 1, 1,
548
+                          c->width  + c->region_border * 2 - 3,
549
+                          c->height + c->region_border * 2 - 3 };
550
+
551
+    xcb_create_gc(c->conn, gc, c->window, mask, values);
552
+
553
+    xcb_poly_rectangle(c->conn, c->window, gc, 1, &r);
554
+}
555
+
556
+static void setup_window(AVFormatContext *s)
557
+{
558
+    XCBGrabContext *c = s->priv_data;
559
+    uint32_t mask     = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
560
+    uint32_t values[] = { 1,
561
+                          XCB_EVENT_MASK_EXPOSURE |
562
+                          XCB_EVENT_MASK_STRUCTURE_NOTIFY };
563
+    xcb_rectangle_t rect = { c->x, c->y, c->width, c->height };
564
+
565
+    c->window = xcb_generate_id(c->conn);
566
+
567
+    xcb_create_window(c->conn, XCB_COPY_FROM_PARENT,
568
+                      c->window,
569
+                      c->screen->root,
570
+                      c->x - c->region_border,
571
+                      c->y - c->region_border,
572
+                      c->width + c->region_border * 2,
573
+                      c->height + c->region_border * 2,
574
+                      0,
575
+                      XCB_WINDOW_CLASS_INPUT_OUTPUT,
576
+                      XCB_COPY_FROM_PARENT,
577
+                      mask, values);
578
+
579
+    xcb_shape_rectangles(c->conn, XCB_SHAPE_SO_SUBTRACT,
580
+                         XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED,
581
+                         c->window,
582
+                         c->region_border, c->region_border,
583
+                         1, &rect);
584
+
585
+    xcb_map_window(c->conn, c->window);
586
+
587
+    draw_rectangle(s);
588
+}
589
+
590
+static av_cold int xcbgrab_read_header(AVFormatContext *s)
591
+{
592
+    XCBGrabContext *c = s->priv_data;
593
+    int screen_num, ret;
594
+    const xcb_setup_t *setup;
595
+
596
+    c->conn = xcb_connect(s->filename, &screen_num);
597
+    if ((ret = xcb_connection_has_error(c->conn))) {
598
+        av_log(s, AV_LOG_ERROR, "Cannot open display %s, error %d.\n",
599
+               s->filename ? s->filename : "default", ret);
600
+        return AVERROR(EIO);
601
+    }
602
+    setup = xcb_get_setup(c->conn);
603
+
604
+    c->screen = get_screen(setup, screen_num);
605
+    if (!c->screen) {
606
+        av_log(s, AV_LOG_ERROR, "The screen %d does not exist.\n",
607
+               screen_num);
608
+        xcbgrab_read_close(s);
609
+        return AVERROR(EIO);
610
+    }
611
+
612
+    c->segment = xcb_generate_id(c->conn);
613
+
614
+    ret = create_stream(s);
615
+
616
+    if (ret < 0) {
617
+        xcbgrab_read_close(s);
618
+        return ret;
619
+    }
620
+
621
+#if CONFIG_LIBXCB_SHM
622
+    c->has_shm = check_shm(c->conn);
623
+#endif
624
+
625
+#if CONFIG_LIBXCB_XFIXES
626
+    if (c->draw_mouse) {
627
+        if (!(c->draw_mouse = check_xfixes(c->conn))) {
628
+            av_log(s, AV_LOG_WARNING,
629
+                   "XFixes not available, cannot draw the mouse.\n");
630
+        }
631
+        if (c->bpp < 24) {
632
+            avpriv_report_missing_feature(s, "%d bits per pixel screen",
633
+                                          c->bpp);
634
+            c->draw_mouse = 0;
635
+        }
636
+    }
637
+#endif
638
+
639
+    if (c->show_region)
640
+        setup_window(s);
641
+
642
+    return 0;
643
+}
644
+
645
+AVInputFormat ff_x11grab_xcb_demuxer = {
646
+    .name           = "x11grab",
647
+    .long_name      = NULL_IF_CONFIG_SMALL("X11 screen capture, using XCB"),
648
+    .priv_data_size = sizeof(XCBGrabContext),
649
+    .read_header    = xcbgrab_read_header,
650
+    .read_packet    = xcbgrab_read_packet,
651
+    .read_close     = xcbgrab_read_close,
652
+    .flags          = AVFMT_NOFILE,
653
+    .priv_class     = &xcbgrab_class,
654
+};