2
* X11 video grab interface
4
* This file is part of FFmpeg.
7
* Copyright (C) 2006 Clemens Fruhwirth <clemens@endorphin.org>
8
* Edouard Gomez <ed.gomez@free.fr>
10
* This file contains code from grab.c:
11
* Copyright (c) 2000-2001 Fabrice Bellard
13
* This file contains code from the xvidcap project:
14
* Copyright (C) 1997-1998 Rasca, Berlin
15
* 2003-2004 Karl H. Beckers, Frankfurt
17
* FFmpeg is free software; you can redistribute it and/or modify
18
* it under the terms of the GNU General Public License as published by
19
* the Free Software Foundation; either version 2 of the License, or
20
* (at your option) any later version.
22
* FFmpeg is distributed in the hope that it will be useful,
23
* but WITHOUT ANY WARRANTY; without even the implied warranty of
24
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
* GNU General Public License for more details.
27
* You should have received a copy of the GNU General Public License along
28
* with FFmpeg; if not, write to the Free Software Foundation, Inc.,
29
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34
* X11 frame device demuxer by Clemens Fruhwirth <clemens@endorphin.org>
35
* and Edouard Gomez <ed.gomez@free.fr>.
41
#include <sys/ioctl.h>
44
#define _LINUX_TIME_H 1
48
#include <X11/Xlibint.h>
49
#include <X11/Xproto.h>
50
#include <X11/Xutil.h>
53
#include <X11/extensions/XShm.h>
56
* X11 Device Demuxer context
58
typedef struct x11_grab_s
60
int frame_size; /**< Size in bytes of a grabbed frame */
61
AVRational time_base; /**< Time base */
62
int64_t time_frame; /**< Current time */
64
int height; /**< Height of the grab frame */
65
int width; /**< Width of the grab frame */
66
int x_off; /**< Horizontal top-left corner coordinate */
67
int y_off; /**< Vertical top-left corner coordinate */
69
Display *dpy; /**< X11 display from which x11grab grabs frames */
70
XImage *image; /**< X11 image holding the grab */
71
int use_shm; /**< !0 when using XShm extension */
72
XShmSegmentInfo shminfo; /**< When using XShm, keeps track of XShm infos */
73
int mouse_warning_shown;
77
* Initializes the x11 grab device demuxer (public device demuxer API).
79
* @param s1 Context from avformat core
80
* @param ap Parameters from avformat core
82
* <li>ENOMEM no memory left</li>
83
* <li>AVERROR_IO other failure case</li>
88
x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
90
x11_grab_t *x11grab = s1->priv_data;
100
param = av_strdup(s1->filename);
101
offset = strchr(param, '+');
103
sscanf(offset, "%d,%d", &x_off, &y_off);
107
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, ap->width, ap->height);
109
dpy = XOpenDisplay(param);
111
av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
115
if (!ap || ap->width <= 0 || ap->height <= 0 || ap->time_base.den <= 0) {
116
av_log(s1, AV_LOG_ERROR, "AVParameters don't have any video size. Use -s.\n");
120
st = av_new_stream(s1, 0);
122
return AVERROR(ENOMEM);
124
av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
126
use_shm = XShmQueryExtension(dpy);
127
av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
130
int scr = XDefaultScreen(dpy);
131
image = XShmCreateImage(dpy,
132
DefaultVisual(dpy, scr),
133
DefaultDepth(dpy, scr),
137
ap->width, ap->height);
138
x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
139
image->bytes_per_line * image->height,
141
if (x11grab->shminfo.shmid == -1) {
142
av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
143
return AVERROR(ENOMEM);
145
x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
146
x11grab->shminfo.readOnly = False;
148
if (!XShmAttach(dpy, &x11grab->shminfo)) {
149
av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
150
/* needs some better error subroutine :) */
154
image = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)),
156
ap->width,ap->height,
160
switch (image->bits_per_pixel) {
162
av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
163
input_pixfmt = PIX_FMT_PAL8;
166
if ( image->red_mask == 0xf800 &&
167
image->green_mask == 0x07e0 &&
168
image->blue_mask == 0x001f ) {
169
av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
170
input_pixfmt = PIX_FMT_RGB565;
171
} else if (image->red_mask == 0x7c00 &&
172
image->green_mask == 0x03e0 &&
173
image->blue_mask == 0x001f ) {
174
av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
175
input_pixfmt = PIX_FMT_RGB555;
177
av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
178
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);
183
if ( image->red_mask == 0xff0000 &&
184
image->green_mask == 0x00ff00 &&
185
image->blue_mask == 0x0000ff ) {
186
input_pixfmt = PIX_FMT_BGR24;
187
} else if ( image->red_mask == 0x0000ff &&
188
image->green_mask == 0x00ff00 &&
189
image->blue_mask == 0xff0000 ) {
190
input_pixfmt = PIX_FMT_RGB24;
192
av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
193
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);
199
GetColorInfo (image, &c_info);
200
if ( c_info.alpha_mask == 0xff000000 && image->green_mask == 0x0000ff00) {
201
/* byte order is relevant here, not endianness
202
* endianness is handled by avcodec, but atm no such thing
203
* as having ABGR, instead of ARGB in a word. Since we
204
* need this for Solaris/SPARC, but need to do the conversion
205
* for every frame we do it outside of this loop, cf. below
206
* this matches both ARGB32 and ABGR32 */
207
input_pixfmt = PIX_FMT_ARGB32;
209
av_log(s1, AV_LOG_ERROR,"image depth %i not supported ... aborting\n", image->bits_per_pixel);
213
input_pixfmt = PIX_FMT_RGB32;
216
av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
220
x11grab->frame_size = ap->width * ap->height * image->bits_per_pixel/8;
222
x11grab->width = ap->width;
223
x11grab->height = ap->height;
224
x11grab->time_base = ap->time_base;
225
x11grab->time_frame = av_gettime() / av_q2d(ap->time_base);
226
x11grab->x_off = x_off;
227
x11grab->y_off = y_off;
228
x11grab->image = image;
229
x11grab->use_shm = use_shm;
230
x11grab->mouse_warning_shown = 0;
232
st->codec->codec_type = CODEC_TYPE_VIDEO;
233
st->codec->codec_id = CODEC_ID_RAWVIDEO;
234
st->codec->width = ap->width;
235
st->codec->height = ap->height;
236
st->codec->pix_fmt = input_pixfmt;
237
st->codec->time_base = ap->time_base;
238
st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(ap->time_base) * 8;
244
* Get pointer coordinates from X11.
246
* @param x Integer where horizontal coordinate will be returned
247
* @param y Integer where vertical coordinate will be returned
248
* @param dpy X11 display from where pointer coordinates are retrieved
249
* @param s1 Context used for logging errors if necessary
252
get_pointer_coordinates(int *x, int *y, Display *dpy, AVFormatContext *s1)
254
Window mrootwindow, childwindow;
257
mrootwindow = DefaultRootWindow(dpy);
259
if (XQueryPointer(dpy, mrootwindow, &mrootwindow, &childwindow,
260
x, y, &dummy, &dummy, (unsigned int*)&dummy)) {
262
x11_grab_t *s = s1->priv_data;
263
if (!s->mouse_warning_shown) {
264
av_log(s1, AV_LOG_INFO, "couldn't find mouse pointer\n");
265
s->mouse_warning_shown = 1;
273
* Mouse painting helper function that applies an 'and' and 'or' mask pair to
274
* '*dst' pixel. It actually draws a mouse pointer pixel to grabbed frame.
276
* @param dst Destination pixel
277
* @param and Part of the mask that must be applied using a bitwise 'and'
279
* @param or Part of the mask that must be applied using a bitwise 'or'
281
* @param bits_per_pixel Bits per pixel used in the grabbed image
284
apply_masks(uint8_t *dst, int and, int or, int bits_per_pixel)
286
switch (bits_per_pixel) {
288
*(uint32_t*)dst = (*(uint32_t*)dst & and) | or;
291
*(uint16_t*)dst = (*(uint16_t*)dst & and) | or;
300
* Paints a mouse pointer in an X11 image.
302
* @param image Image where to paint the mouse pointer
303
* @param s context used to retrieve original grabbing rectangle
305
* @param x Mouse pointer coordinate
306
* @param y Mouse pointer coordinate
309
paint_mouse_pointer(XImage *image, x11_grab_t *s, int x, int y)
311
/* 16x20x1bpp bitmap for the black channel of the mouse pointer */
312
static const uint16_t const mousePointerBlack[] =
314
0x0000, 0x0003, 0x0005, 0x0009, 0x0011,
315
0x0021, 0x0041, 0x0081, 0x0101, 0x0201,
316
0x03c1, 0x0049, 0x0095, 0x0093, 0x0120,
317
0x0120, 0x0240, 0x0240, 0x0380, 0x0000
320
/* 16x20x1bpp bitmap for the white channel of the mouse pointer */
321
static const uint16_t const mousePointerWhite[] =
323
0x0000, 0x0000, 0x0002, 0x0006, 0x000e,
324
0x001e, 0x003e, 0x007e, 0x00fe, 0x01fe,
325
0x003e, 0x0036, 0x0062, 0x0060, 0x00c0,
326
0x00c0, 0x0180, 0x0180, 0x0000, 0x0000
329
int x_off = s->x_off;
330
int y_off = s->y_off;
331
int width = s->width;
332
int height = s->height;
334
if ( x - x_off >= 0 && x < width + x_off
335
&& y - y_off >= 0 && y < height + y_off) {
336
uint8_t *im_data = (uint8_t*)image->data;
341
/* Select correct masks and pixel size */
342
if (image->bits_per_pixel == 8) {
345
masks = (image->red_mask|image->green_mask|image->blue_mask);
347
bytes_per_pixel = image->bits_per_pixel>>3;
349
/* Shift to right line */
350
im_data += image->bytes_per_line * (y - y_off);
351
/* Shift to right pixel in the line */
352
im_data += bytes_per_pixel * (x - x_off);
354
/* Draw the cursor - proper loop */
355
for (line = 0; line < FFMIN(20, (y_off + height) - y); line++) {
356
uint8_t *cursor = im_data;
361
bm_b = mousePointerBlack[line];
362
bm_w = mousePointerWhite[line];
364
for (column = 0; column < FFMIN(16, (x_off + width) - x); column++) {
365
apply_masks(cursor, ~(masks*(bm_b&1)), masks*(bm_w&1),
366
image->bits_per_pixel);
367
cursor += bytes_per_pixel;
371
im_data += image->bytes_per_line;
378
* Reads new data in the image structure.
380
* @param dpy X11 display to grab from
382
* @param image Image where the grab will be put
383
* @param x Top-Left grabbing rectangle horizontal coordinate
384
* @param y Top-Left grabbing rectangle vertical coordinate
385
* @return 0 if error, !0 if successful
388
xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
399
GetReq(GetImage, req);
401
/* First set up the standard stuff in the request */
405
req->width = image->width;
406
req->height = image->height;
407
req->planeMask = (unsigned int)AllPlanes;
408
req->format = ZPixmap;
410
if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
416
nbytes = (long)rep.length << 2;
417
_XReadPad(dpy, image->data, nbytes);
425
* Grabs a frame from x11 (public device demuxer API).
427
* @param s1 Context from avformat core
428
* @param pkt Packet holding the brabbed frame
429
* @return frame size in bytes
432
x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
434
x11_grab_t *s = s1->priv_data;
435
Display *dpy = s->dpy;
436
XImage *image = s->image;
437
int x_off = s->x_off;
438
int y_off = s->y_off;
440
int64_t curtime, delay;
443
/* Calculate the time of the next frame */
444
s->time_frame += INT64_C(1000000);
446
/* wait based on the frame rate */
448
curtime = av_gettime();
449
delay = s->time_frame * av_q2d(s->time_base) - curtime;
451
if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
452
s->time_frame += INT64_C(1000000);
456
ts.tv_sec = delay / 1000000;
457
ts.tv_nsec = (delay % 1000000) * 1000;
458
nanosleep(&ts, NULL);
461
if (av_new_packet(pkt, s->frame_size) < 0) {
468
if (!XShmGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off, AllPlanes)) {
469
av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
472
if (!xget_zpixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off)) {
473
av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
478
int pointer_x, pointer_y;
479
get_pointer_coordinates(&pointer_x, &pointer_y, dpy, s1);
480
paint_mouse_pointer(image, s, pointer_x, pointer_y);
484
/* XXX: avoid memcpy */
485
memcpy(pkt->data, image->data, s->frame_size);
486
return s->frame_size;
490
* Closes x11 frame grabber (public device demuxer API).
492
* @param s1 Context from avformat core
493
* @return 0 success, !0 failure
496
x11grab_read_close(AVFormatContext *s1)
498
x11_grab_t *x11grab = s1->priv_data;
500
/* Detach cleanly from shared mem */
501
if (x11grab->use_shm) {
502
XShmDetach(x11grab->dpy, &x11grab->shminfo);
503
shmdt(x11grab->shminfo.shmaddr);
504
shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL);
507
/* Destroy X11 image */
508
if (x11grab->image) {
509
XDestroyImage(x11grab->image);
510
x11grab->image = NULL;
513
/* Free X11 display */
514
XCloseDisplay(x11grab->dpy);
518
/** x11 grabber device demuxer declaration */
519
AVInputFormat x11_grab_device_demuxer =
528
.flags = AVFMT_NOFILE,