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
34 * X11 frame device demuxer by Clemens Fruhwirth <clemens@endorphin.org>
35 * and Edouard Gomez <ed.gomez@free.fr>.
39 #include "libavformat/avformat.h"
42 #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>AVERROR(ENOMEM) no memory left</li>
83 * <li>AVERROR(EIO) 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 video size and/or rate. Use -s and -r.\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 to paint the mouse pointer to
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
,