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