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
28 * along with FFmpeg; if not, write to the Free Software
29 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33 * @file libavdevice/x11grab.c
34 * X11 frame device demuxer by Clemens Fruhwirth <clemens@endorphin.org>
35 * and Edouard Gomez <ed.gomez@free.fr>.
39 #include "libavformat/avformat.h"
43 #include <X11/Xlibint.h>
44 #include <X11/Xproto.h>
45 #include <X11/Xutil.h>
47 #include <X11/extensions/XShm.h>
50 * X11 Device Demuxer context
54 int frame_size
; /**< Size in bytes of a grabbed frame */
55 AVRational time_base
; /**< Time base */
56 int64_t time_frame
; /**< Current time */
58 int height
; /**< Height of the grab frame */
59 int width
; /**< Width of the grab frame */
60 int x_off
; /**< Horizontal top-left corner coordinate */
61 int y_off
; /**< Vertical top-left corner coordinate */
63 Display
*dpy
; /**< X11 display from which x11grab grabs frames */
64 XImage
*image
; /**< X11 image holding the grab */
65 int use_shm
; /**< !0 when using XShm extension */
66 XShmSegmentInfo shminfo
; /**< When using XShm, keeps track of XShm infos */
67 int mouse_warning_shown
;
71 * Initializes the x11 grab device demuxer (public device demuxer API).
73 * @param s1 Context from avformat core
74 * @param ap Parameters from avformat core
76 * <li>AVERROR(ENOMEM) no memory left</li>
77 * <li>AVERROR(EIO) other failure case</li>
82 x11grab_read_header(AVFormatContext
*s1
, AVFormatParameters
*ap
)
84 struct x11_grab
*x11grab
= s1
->priv_data
;
94 param
= av_strdup(s1
->filename
);
95 offset
= strchr(param
, '+');
97 sscanf(offset
, "%d,%d", &x_off
, &y_off
);
101 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
);
103 dpy
= XOpenDisplay(param
);
105 av_log(s1
, AV_LOG_ERROR
, "Could not open X display.\n");
109 if (!ap
|| ap
->width
<= 0 || ap
->height
<= 0 || ap
->time_base
.den
<= 0) {
110 av_log(s1
, AV_LOG_ERROR
, "AVParameters don't have video size and/or rate. Use -s and -r.\n");
114 st
= av_new_stream(s1
, 0);
116 return AVERROR(ENOMEM
);
118 av_set_pts_info(st
, 64, 1, 1000000); /* 64 bits pts in us */
120 use_shm
= XShmQueryExtension(dpy
);
121 av_log(s1
, AV_LOG_INFO
, "shared memory extension %s found\n", use_shm
? "" : "not");
124 int scr
= XDefaultScreen(dpy
);
125 image
= XShmCreateImage(dpy
,
126 DefaultVisual(dpy
, scr
),
127 DefaultDepth(dpy
, scr
),
131 ap
->width
, ap
->height
);
132 x11grab
->shminfo
.shmid
= shmget(IPC_PRIVATE
,
133 image
->bytes_per_line
* image
->height
,
135 if (x11grab
->shminfo
.shmid
== -1) {
136 av_log(s1
, AV_LOG_ERROR
, "Fatal: Can't get shared memory!\n");
137 return AVERROR(ENOMEM
);
139 x11grab
->shminfo
.shmaddr
= image
->data
= shmat(x11grab
->shminfo
.shmid
, 0, 0);
140 x11grab
->shminfo
.readOnly
= False
;
142 if (!XShmAttach(dpy
, &x11grab
->shminfo
)) {
143 av_log(s1
, AV_LOG_ERROR
, "Fatal: Failed to attach shared memory!\n");
144 /* needs some better error subroutine :) */
148 image
= XGetImage(dpy
, RootWindow(dpy
, DefaultScreen(dpy
)),
150 ap
->width
,ap
->height
,
154 switch (image
->bits_per_pixel
) {
156 av_log (s1
, AV_LOG_DEBUG
, "8 bit palette\n");
157 input_pixfmt
= PIX_FMT_PAL8
;
160 if ( image
->red_mask
== 0xf800 &&
161 image
->green_mask
== 0x07e0 &&
162 image
->blue_mask
== 0x001f ) {
163 av_log (s1
, AV_LOG_DEBUG
, "16 bit RGB565\n");
164 input_pixfmt
= PIX_FMT_RGB565
;
165 } else if (image
->red_mask
== 0x7c00 &&
166 image
->green_mask
== 0x03e0 &&
167 image
->blue_mask
== 0x001f ) {
168 av_log(s1
, AV_LOG_DEBUG
, "16 bit RGB555\n");
169 input_pixfmt
= PIX_FMT_RGB555
;
171 av_log(s1
, AV_LOG_ERROR
, "RGB ordering at image depth %i not supported ... aborting\n", image
->bits_per_pixel
);
172 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
);
177 if ( image
->red_mask
== 0xff0000 &&
178 image
->green_mask
== 0x00ff00 &&
179 image
->blue_mask
== 0x0000ff ) {
180 input_pixfmt
= PIX_FMT_BGR24
;
181 } else if ( image
->red_mask
== 0x0000ff &&
182 image
->green_mask
== 0x00ff00 &&
183 image
->blue_mask
== 0xff0000 ) {
184 input_pixfmt
= PIX_FMT_RGB24
;
186 av_log(s1
, AV_LOG_ERROR
,"rgb ordering at image depth %i not supported ... aborting\n", image
->bits_per_pixel
);
187 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
);
193 GetColorInfo (image
, &c_info
);
194 if ( c_info
.alpha_mask
== 0xff000000 && image
->green_mask
== 0x0000ff00) {
195 /* byte order is relevant here, not endianness
196 * endianness is handled by avcodec, but atm no such thing
197 * as having ABGR, instead of ARGB in a word. Since we
198 * need this for Solaris/SPARC, but need to do the conversion
199 * for every frame we do it outside of this loop, cf. below
200 * this matches both ARGB32 and ABGR32 */
201 input_pixfmt
= PIX_FMT_ARGB32
;
203 av_log(s1
, AV_LOG_ERROR
,"image depth %i not supported ... aborting\n", image
->bits_per_pixel
);
207 input_pixfmt
= PIX_FMT_RGB32
;
210 av_log(s1
, AV_LOG_ERROR
, "image depth %i not supported ... aborting\n", image
->bits_per_pixel
);
214 x11grab
->frame_size
= ap
->width
* ap
->height
* image
->bits_per_pixel
/8;
216 x11grab
->width
= ap
->width
;
217 x11grab
->height
= ap
->height
;
218 x11grab
->time_base
= ap
->time_base
;
219 x11grab
->time_frame
= av_gettime() / av_q2d(ap
->time_base
);
220 x11grab
->x_off
= x_off
;
221 x11grab
->y_off
= y_off
;
222 x11grab
->image
= image
;
223 x11grab
->use_shm
= use_shm
;
224 x11grab
->mouse_warning_shown
= 0;
226 st
->codec
->codec_type
= CODEC_TYPE_VIDEO
;
227 st
->codec
->codec_id
= CODEC_ID_RAWVIDEO
;
228 st
->codec
->width
= ap
->width
;
229 st
->codec
->height
= ap
->height
;
230 st
->codec
->pix_fmt
= input_pixfmt
;
231 st
->codec
->time_base
= ap
->time_base
;
232 st
->codec
->bit_rate
= x11grab
->frame_size
* 1/av_q2d(ap
->time_base
) * 8;
238 * Get pointer coordinates from X11.
240 * @param x Integer where horizontal coordinate will be returned
241 * @param y Integer where vertical coordinate will be returned
242 * @param dpy X11 display from where pointer coordinates are retrieved
243 * @param s1 Context used for logging errors if necessary
246 get_pointer_coordinates(int *x
, int *y
, Display
*dpy
, AVFormatContext
*s1
)
248 Window mrootwindow
, childwindow
;
251 mrootwindow
= DefaultRootWindow(dpy
);
253 if (XQueryPointer(dpy
, mrootwindow
, &mrootwindow
, &childwindow
,
254 x
, y
, &dummy
, &dummy
, (unsigned int*)&dummy
)) {
256 struct x11_grab
*s
= s1
->priv_data
;
257 if (!s
->mouse_warning_shown
) {
258 av_log(s1
, AV_LOG_INFO
, "couldn't find mouse pointer\n");
259 s
->mouse_warning_shown
= 1;
267 * Mouse painting helper function that applies an 'and' and 'or' mask pair to
268 * '*dst' pixel. It actually draws a mouse pointer pixel to grabbed frame.
270 * @param dst Destination pixel
271 * @param and Part of the mask that must be applied using a bitwise 'and'
273 * @param or Part of the mask that must be applied using a bitwise 'or'
275 * @param bits_per_pixel Bits per pixel used in the grabbed image
278 apply_masks(uint8_t *dst
, int and, int or, int bits_per_pixel
)
280 switch (bits_per_pixel
) {
282 *(uint32_t*)dst
= (*(uint32_t*)dst
& and) | or;
285 *(uint16_t*)dst
= (*(uint16_t*)dst
& and) | or;
294 * Paints a mouse pointer in an X11 image.
296 * @param image image to paint the mouse pointer to
297 * @param s context used to retrieve original grabbing rectangle
299 * @param x Mouse pointer coordinate
300 * @param y Mouse pointer coordinate
303 paint_mouse_pointer(XImage
*image
, struct x11_grab
*s
, int x
, int y
)
305 /* 16x20x1bpp bitmap for the black channel of the mouse pointer */
306 static const uint16_t const mousePointerBlack
[] =
308 0x0000, 0x0003, 0x0005, 0x0009, 0x0011,
309 0x0021, 0x0041, 0x0081, 0x0101, 0x0201,
310 0x03c1, 0x0049, 0x0095, 0x0093, 0x0120,
311 0x0120, 0x0240, 0x0240, 0x0380, 0x0000
314 /* 16x20x1bpp bitmap for the white channel of the mouse pointer */
315 static const uint16_t const mousePointerWhite
[] =
317 0x0000, 0x0000, 0x0002, 0x0006, 0x000e,
318 0x001e, 0x003e, 0x007e, 0x00fe, 0x01fe,
319 0x003e, 0x0036, 0x0062, 0x0060, 0x00c0,
320 0x00c0, 0x0180, 0x0180, 0x0000, 0x0000
323 int x_off
= s
->x_off
;
324 int y_off
= s
->y_off
;
325 int width
= s
->width
;
326 int height
= s
->height
;
328 if ( x
- x_off
>= 0 && x
< width
+ x_off
329 && y
- y_off
>= 0 && y
< height
+ y_off
) {
330 uint8_t *im_data
= (uint8_t*)image
->data
;
335 /* Select correct masks and pixel size */
336 if (image
->bits_per_pixel
== 8) {
339 masks
= (image
->red_mask
|image
->green_mask
|image
->blue_mask
);
341 bytes_per_pixel
= image
->bits_per_pixel
>>3;
343 /* Shift to right line */
344 im_data
+= image
->bytes_per_line
* (y
- y_off
);
345 /* Shift to right pixel in the line */
346 im_data
+= bytes_per_pixel
* (x
- x_off
);
348 /* Draw the cursor - proper loop */
349 for (line
= 0; line
< FFMIN(20, (y_off
+ height
) - y
); line
++) {
350 uint8_t *cursor
= im_data
;
355 bm_b
= mousePointerBlack
[line
];
356 bm_w
= mousePointerWhite
[line
];
358 for (column
= 0; column
< FFMIN(16, (x_off
+ width
) - x
); column
++) {
359 apply_masks(cursor
, ~(masks
*(bm_b
&1)), masks
*(bm_w
&1),
360 image
->bits_per_pixel
);
361 cursor
+= bytes_per_pixel
;
365 im_data
+= image
->bytes_per_line
;
372 * Reads new data in the image structure.
374 * @param dpy X11 display to grab from
376 * @param image Image where the grab will be put
377 * @param x Top-Left grabbing rectangle horizontal coordinate
378 * @param y Top-Left grabbing rectangle vertical coordinate
379 * @return 0 if error, !0 if successful
382 xget_zpixmap(Display
*dpy
, Drawable d
, XImage
*image
, int x
, int y
)
393 GetReq(GetImage
, req
);
395 /* First set up the standard stuff in the request */
399 req
->width
= image
->width
;
400 req
->height
= image
->height
;
401 req
->planeMask
= (unsigned int)AllPlanes
;
402 req
->format
= ZPixmap
;
404 if (!_XReply(dpy
, (xReply
*)&rep
, 0, xFalse
) || !rep
.length
) {
410 nbytes
= (long)rep
.length
<< 2;
411 _XReadPad(dpy
, image
->data
, nbytes
);
419 * Grabs a frame from x11 (public device demuxer API).
421 * @param s1 Context from avformat core
422 * @param pkt Packet holding the brabbed frame
423 * @return frame size in bytes
426 x11grab_read_packet(AVFormatContext
*s1
, AVPacket
*pkt
)
428 struct x11_grab
*s
= s1
->priv_data
;
429 Display
*dpy
= s
->dpy
;
430 XImage
*image
= s
->image
;
431 int x_off
= s
->x_off
;
432 int y_off
= s
->y_off
;
434 int64_t curtime
, delay
;
437 /* Calculate the time of the next frame */
438 s
->time_frame
+= INT64_C(1000000);
440 /* wait based on the frame rate */
442 curtime
= av_gettime();
443 delay
= s
->time_frame
* av_q2d(s
->time_base
) - curtime
;
445 if (delay
< INT64_C(-1000000) * av_q2d(s
->time_base
)) {
446 s
->time_frame
+= INT64_C(1000000);
450 ts
.tv_sec
= delay
/ 1000000;
451 ts
.tv_nsec
= (delay
% 1000000) * 1000;
452 nanosleep(&ts
, NULL
);
455 if (av_new_packet(pkt
, s
->frame_size
) < 0) {
462 if (!XShmGetImage(dpy
, RootWindow(dpy
, DefaultScreen(dpy
)), image
, x_off
, y_off
, AllPlanes
)) {
463 av_log (s1
, AV_LOG_INFO
, "XShmGetImage() failed\n");
466 if (!xget_zpixmap(dpy
, RootWindow(dpy
, DefaultScreen(dpy
)), image
, x_off
, y_off
)) {
467 av_log (s1
, AV_LOG_INFO
, "XGetZPixmap() failed\n");
472 int pointer_x
, pointer_y
;
473 get_pointer_coordinates(&pointer_x
, &pointer_y
, dpy
, s1
);
474 paint_mouse_pointer(image
, s
, pointer_x
, pointer_y
);
478 /* XXX: avoid memcpy */
479 memcpy(pkt
->data
, image
->data
, s
->frame_size
);
480 return s
->frame_size
;
484 * Closes x11 frame grabber (public device demuxer API).
486 * @param s1 Context from avformat core
487 * @return 0 success, !0 failure
490 x11grab_read_close(AVFormatContext
*s1
)
492 struct x11_grab
*x11grab
= s1
->priv_data
;
494 /* Detach cleanly from shared mem */
495 if (x11grab
->use_shm
) {
496 XShmDetach(x11grab
->dpy
, &x11grab
->shminfo
);
497 shmdt(x11grab
->shminfo
.shmaddr
);
498 shmctl(x11grab
->shminfo
.shmid
, IPC_RMID
, NULL
);
501 /* Destroy X11 image */
502 if (x11grab
->image
) {
503 XDestroyImage(x11grab
->image
);
504 x11grab
->image
= NULL
;
507 /* Free X11 display */
508 XCloseDisplay(x11grab
->dpy
);
512 /** x11 grabber device demuxer declaration */
513 AVInputFormat x11_grab_device_demuxer
=
516 NULL_IF_CONFIG_SMALL("X11grab"),
517 sizeof(struct x11_grab
),
522 .flags
= AVFMT_NOFILE
,