1/* 2 * X11 video grab interface 3 * 4 * This file is part of FFmpeg. 5 * 6 * FFmpeg integration: 7 * Copyright (C) 2006 Clemens Fruhwirth <clemens@endorphin.org> 8 * Edouard Gomez <ed.gomez@free.fr> 9 * 10 * This file contains code from grab.c: 11 * Copyright (c) 2000-2001 Fabrice Bellard 12 * 13 * This file contains code from the xvidcap project: 14 * Copyright (C) 1997-1998 Rasca, Berlin 15 * 2003-2004 Karl H. Beckers, Frankfurt 16 * 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. 21 * 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. 26 * 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 30 */ 31 32/** 33 * @file 34 * X11 frame device demuxer 35 * @author Clemens Fruhwirth <clemens@endorphin.org> 36 * @author Edouard Gomez <ed.gomez@free.fr> 37 */ 38 39#include "config.h" 40#include "libavformat/internal.h" 41#include "libavutil/log.h" 42#include "libavutil/opt.h" 43#include "libavutil/parseutils.h" 44#include "libavutil/time.h" 45#include <time.h> 46#include <X11/cursorfont.h> 47#include <X11/X.h> 48#include <X11/Xlib.h> 49#include <X11/Xlibint.h> 50#include <X11/Xproto.h> 51#include <X11/Xutil.h> 52#include <sys/shm.h> 53#include <X11/extensions/shape.h> 54#include <X11/extensions/XShm.h> 55#include <X11/extensions/Xfixes.h> 56#include "avdevice.h" 57 58/** 59 * X11 Device Demuxer context 60 */ 61struct x11grab { 62 const AVClass *class; /**< Class for private options. */ 63 int frame_size; /**< Size in bytes of a grabbed frame */ 64 AVRational time_base; /**< Time base */ 65 int64_t time_frame; /**< Current time */ 66 67 int width; /**< Width of the grab frame */ 68 int height; /**< Height of the grab frame */ 69 int x_off; /**< Horizontal top-left corner coordinate */ 70 int y_off; /**< Vertical top-left corner coordinate */ 71 72 Display *dpy; /**< X11 display from which x11grab grabs frames */ 73 XImage *image; /**< X11 image holding the grab */ 74 int use_shm; /**< !0 when using XShm extension */ 75 XShmSegmentInfo shminfo; /**< When using XShm, keeps track of XShm infos */ 76 int draw_mouse; /**< Set by a private option. */ 77 int follow_mouse; /**< Set by a private option. */ 78 int show_region; /**< set by a private option. */ 79 AVRational framerate; /**< Set by a private option. */ 80 int palette_changed; 81 uint32_t palette[256]; 82 83 Cursor c; 84 Window region_win; /**< This is used by show_region option. */ 85}; 86 87#define REGION_WIN_BORDER 3 88/** 89 * Draw grabbing region window 90 * 91 * @param s x11grab context 92 */ 93static void 94x11grab_draw_region_win(struct x11grab *s) 95{ 96 Display *dpy = s->dpy; 97 int screen; 98 Window win = s->region_win; 99 GC gc; 100 101 screen = DefaultScreen(dpy); 102 gc = XCreateGC(dpy, win, 0, 0); 103 XSetForeground(dpy, gc, WhitePixel(dpy, screen)); 104 XSetBackground(dpy, gc, BlackPixel(dpy, screen)); 105 XSetLineAttributes(dpy, gc, REGION_WIN_BORDER, LineDoubleDash, 0, 0); 106 XDrawRectangle(dpy, win, gc, 107 1, 1, 108 (s->width + REGION_WIN_BORDER * 2) - 1 * 2 - 1, 109 (s->height + REGION_WIN_BORDER * 2) - 1 * 2 - 1); 110 XFreeGC(dpy, gc); 111} 112 113/** 114 * Initialize grabbing region window 115 * 116 * @param s x11grab context 117 */ 118static void 119x11grab_region_win_init(struct x11grab *s) 120{ 121 Display *dpy = s->dpy; 122 int screen; 123 XSetWindowAttributes attribs; 124 XRectangle rect; 125 126 screen = DefaultScreen(dpy); 127 attribs.override_redirect = True; 128 s->region_win = XCreateWindow(dpy, RootWindow(dpy, screen), 129 s->x_off - REGION_WIN_BORDER, 130 s->y_off - REGION_WIN_BORDER, 131 s->width + REGION_WIN_BORDER * 2, 132 s->height + REGION_WIN_BORDER * 2, 133 0, CopyFromParent, 134 InputOutput, CopyFromParent, 135 CWOverrideRedirect, &attribs); 136 rect.x = 0; 137 rect.y = 0; 138 rect.width = s->width; 139 rect.height = s->height; 140 XShapeCombineRectangles(dpy, s->region_win, 141 ShapeBounding, REGION_WIN_BORDER, REGION_WIN_BORDER, 142 &rect, 1, ShapeSubtract, 0); 143 XMapWindow(dpy, s->region_win); 144 XSelectInput(dpy, s->region_win, ExposureMask | StructureNotifyMask); 145 x11grab_draw_region_win(s); 146} 147 148/** 149 * Initialize the x11 grab device demuxer (public device demuxer API). 150 * 151 * @param s1 Context from avformat core 152 * @return <ul> 153 * <li>AVERROR(ENOMEM) no memory left</li> 154 * <li>AVERROR(EIO) other failure case</li> 155 * <li>0 success</li> 156 * </ul> 157 */ 158static int 159x11grab_read_header(AVFormatContext *s1) 160{ 161 struct x11grab *x11grab = s1->priv_data; 162 Display *dpy; 163 AVStream *st = NULL; 164 enum AVPixelFormat input_pixfmt; 165 XImage *image; 166 int x_off = 0; 167 int y_off = 0; 168 int screen; 169 int use_shm; 170 char *dpyname, *offset; 171 int ret = 0; 172 Colormap color_map; 173 XColor color[256]; 174 int i; 175 176 dpyname = av_strdup(s1->filename); 177 if (!dpyname) 178 goto out; 179 180 offset = strchr(dpyname, '+'); 181 if (offset) { 182 sscanf(offset, "%d,%d", &x_off, &y_off); 183 if (strstr(offset, "nomouse")) { 184 av_log(s1, AV_LOG_WARNING, 185 "'nomouse' specification in argument is deprecated: " 186 "use 'draw_mouse' option with value 0 instead\n"); 187 x11grab->draw_mouse = 0; 188 } 189 *offset= 0; 190 } 191 192 av_log(s1, AV_LOG_INFO, "device: %s -> display: %s x: %d y: %d width: %d height: %d\n", 193 s1->filename, dpyname, x_off, y_off, x11grab->width, x11grab->height); 194 195 dpy = XOpenDisplay(dpyname); 196 av_freep(&dpyname); 197 if(!dpy) { 198 av_log(s1, AV_LOG_ERROR, "Could not open X display.\n"); 199 ret = AVERROR(EIO); 200 goto out; 201 } 202 203 st = avformat_new_stream(s1, NULL); 204 if (!st) { 205 ret = AVERROR(ENOMEM); 206 goto out; 207 } 208 avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ 209 210 screen = DefaultScreen(dpy); 211 212 if (x11grab->follow_mouse) { 213 int screen_w, screen_h; 214 Window w; 215 216 screen_w = DisplayWidth(dpy, screen); 217 screen_h = DisplayHeight(dpy, screen); 218 XQueryPointer(dpy, RootWindow(dpy, screen), &w, &w, &x_off, &y_off, &ret, &ret, &ret); 219 x_off -= x11grab->width / 2; 220 y_off -= x11grab->height / 2; 221 x_off = FFMIN(FFMAX(x_off, 0), screen_w - x11grab->width); 222 y_off = FFMIN(FFMAX(y_off, 0), screen_h - x11grab->height); 223 av_log(s1, AV_LOG_INFO, "followmouse is enabled, resetting grabbing region to x: %d y: %d\n", x_off, y_off); 224 } 225 226 use_shm = XShmQueryExtension(dpy); 227 av_log(s1, AV_LOG_INFO, "shared memory extension%s found\n", use_shm ? "" : " not"); 228 229 if(use_shm) { 230 int scr = XDefaultScreen(dpy); 231 image = XShmCreateImage(dpy, 232 DefaultVisual(dpy, scr), 233 DefaultDepth(dpy, scr), 234 ZPixmap, 235 NULL, 236 &x11grab->shminfo, 237 x11grab->width, x11grab->height); 238 x11grab->shminfo.shmid = shmget(IPC_PRIVATE, 239 image->bytes_per_line * image->height, 240 IPC_CREAT|0777); 241 if (x11grab->shminfo.shmid == -1) { 242 av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n"); 243 ret = AVERROR(ENOMEM); 244 goto out; 245 } 246 x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0); 247 x11grab->shminfo.readOnly = False; 248 249 if (!XShmAttach(dpy, &x11grab->shminfo)) { 250 av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n"); 251 /* needs some better error subroutine :) */ 252 ret = AVERROR(EIO); 253 goto out; 254 } 255 } else { 256 image = XGetImage(dpy, RootWindow(dpy, screen), 257 x_off,y_off, 258 x11grab->width, x11grab->height, 259 AllPlanes, ZPixmap); 260 } 261 262 switch (image->bits_per_pixel) { 263 case 8: 264 av_log (s1, AV_LOG_DEBUG, "8 bit palette\n"); 265 input_pixfmt = AV_PIX_FMT_PAL8; 266 color_map = DefaultColormap(dpy, screen); 267 for (i = 0; i < 256; ++i) 268 color[i].pixel = i; 269 XQueryColors(dpy, color_map, color, 256); 270 for (i = 0; i < 256; ++i) 271 x11grab->palette[i] = (color[i].red & 0xFF00) << 8 | 272 (color[i].green & 0xFF00) | 273 (color[i].blue & 0xFF00) >> 8; 274 x11grab->palette_changed = 1; 275 break; 276 case 16: 277 if ( image->red_mask == 0xf800 && 278 image->green_mask == 0x07e0 && 279 image->blue_mask == 0x001f ) { 280 av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n"); 281 input_pixfmt = AV_PIX_FMT_RGB565; 282 } else if (image->red_mask == 0x7c00 && 283 image->green_mask == 0x03e0 && 284 image->blue_mask == 0x001f ) { 285 av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n"); 286 input_pixfmt = AV_PIX_FMT_RGB555; 287 } else { 288 av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel); 289 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); 290 ret = AVERROR(EIO); 291 goto out; 292 } 293 break; 294 case 24: 295 if ( image->red_mask == 0xff0000 && 296 image->green_mask == 0x00ff00 && 297 image->blue_mask == 0x0000ff ) { 298 input_pixfmt = AV_PIX_FMT_BGR24; 299 } else if ( image->red_mask == 0x0000ff && 300 image->green_mask == 0x00ff00 && 301 image->blue_mask == 0xff0000 ) { 302 input_pixfmt = AV_PIX_FMT_RGB24; 303 } else { 304 av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel); 305 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); 306 ret = AVERROR(EIO); 307 goto out; 308 } 309 break; 310 case 32: 311 input_pixfmt = AV_PIX_FMT_0RGB32; 312 break; 313 default: 314 av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel); 315 ret = AVERROR(EINVAL); 316 goto out; 317 } 318 319 x11grab->frame_size = x11grab->width * x11grab->height * image->bits_per_pixel/8; 320 x11grab->dpy = dpy; 321 x11grab->time_base = av_inv_q(x11grab->framerate); 322 x11grab->time_frame = av_gettime() / av_q2d(x11grab->time_base); 323 x11grab->x_off = x_off; 324 x11grab->y_off = y_off; 325 x11grab->image = image; 326 x11grab->use_shm = use_shm; 327 328 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 329 st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; 330 st->codec->width = x11grab->width; 331 st->codec->height = x11grab->height; 332 st->codec->pix_fmt = input_pixfmt; 333 st->codec->time_base = x11grab->time_base; 334 st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(x11grab->time_base) * 8; 335 336out: 337 av_free(dpyname); 338 return ret; 339} 340 341/** 342 * Paint a mouse pointer in an X11 image. 343 * 344 * @param image image to paint the mouse pointer to 345 * @param s context used to retrieve original grabbing rectangle 346 * coordinates 347 */ 348static void 349paint_mouse_pointer(XImage *image, struct x11grab *s) 350{ 351 int x_off = s->x_off; 352 int y_off = s->y_off; 353 int width = s->width; 354 int height = s->height; 355 Display *dpy = s->dpy; 356 XFixesCursorImage *xcim; 357 int x, y; 358 int line, column; 359 int to_line, to_column; 360 int pixstride = image->bits_per_pixel >> 3; 361 /* Warning: in its insanity, xlib provides unsigned image data through a 362 * char* pointer, so we have to make it uint8_t to make things not break. 363 * Anyone who performs further investigation of the xlib API likely risks 364 * permanent brain damage. */ 365 uint8_t *pix = image->data; 366 Window w; 367 XSetWindowAttributes attr; 368 369 /* Code doesn't currently support 16-bit or PAL8 */ 370 if (image->bits_per_pixel != 24 && image->bits_per_pixel != 32) 371 return; 372 373 if(!s->c) 374 s->c = XCreateFontCursor(dpy, XC_left_ptr); 375 w = DefaultRootWindow(dpy); 376 attr.cursor = s->c; 377 XChangeWindowAttributes(dpy, w, CWCursor, &attr); 378 379 xcim = XFixesGetCursorImage(dpy); 380 381 x = xcim->x - xcim->xhot; 382 y = xcim->y - xcim->yhot; 383 384 to_line = FFMIN((y + xcim->height), (height + y_off)); 385 to_column = FFMIN((x + xcim->width), (width + x_off)); 386 387 for (line = FFMAX(y, y_off); line < to_line; line++) { 388 for (column = FFMAX(x, x_off); column < to_column; column++) { 389 int xcim_addr = (line - y) * xcim->width + column - x; 390 int image_addr = ((line - y_off) * width + column - x_off) * pixstride; 391 int r = (uint8_t)(xcim->pixels[xcim_addr] >> 0); 392 int g = (uint8_t)(xcim->pixels[xcim_addr] >> 8); 393 int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16); 394 int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24); 395 396 if (a == 255) { 397 pix[image_addr+0] = r; 398 pix[image_addr+1] = g; 399 pix[image_addr+2] = b; 400 } else if (a) { 401 /* pixel values from XFixesGetCursorImage come premultiplied by alpha */ 402 pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255; 403 pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255; 404 pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255; 405 } 406 } 407 } 408 409 XFree(xcim); 410 xcim = NULL; 411} 412 413 414/** 415 * Read new data in the image structure. 416 * 417 * @param dpy X11 display to grab from 418 * @param d 419 * @param image Image where the grab will be put 420 * @param x Top-Left grabbing rectangle horizontal coordinate 421 * @param y Top-Left grabbing rectangle vertical coordinate 422 * @return 0 if error, !0 if successful 423 */ 424static int 425xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y) 426{ 427 xGetImageReply rep; 428 xGetImageReq *req; 429 long nbytes; 430 431 if (!image) { 432 return 0; 433 } 434 435 LockDisplay(dpy); 436 GetReq(GetImage, req); 437 438 /* First set up the standard stuff in the request */ 439 req->drawable = d; 440 req->x = x; 441 req->y = y; 442 req->width = image->width; 443 req->height = image->height; 444 req->planeMask = (unsigned int)AllPlanes; 445 req->format = ZPixmap; 446 447 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) { 448 UnlockDisplay(dpy); 449 SyncHandle(); 450 return 0; 451 } 452 453 nbytes = (long)rep.length << 2; 454 _XReadPad(dpy, image->data, nbytes); 455 456 UnlockDisplay(dpy); 457 SyncHandle(); 458 return 1; 459} 460 461/** 462 * Grab a frame from x11 (public device demuxer API). 463 * 464 * @param s1 Context from avformat core 465 * @param pkt Packet holding the brabbed frame 466 * @return frame size in bytes 467 */ 468static int 469x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt) 470{ 471 struct x11grab *s = s1->priv_data; 472 Display *dpy = s->dpy; 473 XImage *image = s->image; 474 int x_off = s->x_off; 475 int y_off = s->y_off; 476 477 int screen; 478 Window root; 479 int follow_mouse = s->follow_mouse; 480 481 int64_t curtime, delay; 482 struct timespec ts; 483 484 /* Calculate the time of the next frame */ 485 s->time_frame += INT64_C(1000000); 486 487 /* wait based on the frame rate */ 488 for(;;) { 489 curtime = av_gettime(); 490 delay = s->time_frame * av_q2d(s->time_base) - curtime; 491 if (delay <= 0) { 492 if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) { 493 s->time_frame += INT64_C(1000000); 494 } 495 break; 496 } 497 ts.tv_sec = delay / 1000000; 498 ts.tv_nsec = (delay % 1000000) * 1000; 499 nanosleep(&ts, NULL); 500 } 501 502 av_init_packet(pkt); 503 pkt->data = image->data; 504 pkt->size = s->frame_size; 505 pkt->pts = curtime; 506 if (s->palette_changed) { 507 uint8_t *pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, 508 AVPALETTE_SIZE); 509 if (!pal) { 510 av_log(s, AV_LOG_ERROR, "Cannot append palette to packet\n"); 511 } else { 512 memcpy(pal, s->palette, AVPALETTE_SIZE); 513 s->palette_changed = 0; 514 } 515 } 516 517 screen = DefaultScreen(dpy); 518 root = RootWindow(dpy, screen); 519 if (follow_mouse) { 520 int screen_w, screen_h; 521 int pointer_x, pointer_y, _; 522 Window w; 523 524 screen_w = DisplayWidth(dpy, screen); 525 screen_h = DisplayHeight(dpy, screen); 526 XQueryPointer(dpy, root, &w, &w, &pointer_x, &pointer_y, &_, &_, &_); 527 if (follow_mouse == -1) { 528 // follow the mouse, put it at center of grabbing region 529 x_off += pointer_x - s->width / 2 - x_off; 530 y_off += pointer_y - s->height / 2 - y_off; 531 } else { 532 // follow the mouse, but only move the grabbing region when mouse 533 // reaches within certain pixels to the edge. 534 if (pointer_x > x_off + s->width - follow_mouse) { 535 x_off += pointer_x - (x_off + s->width - follow_mouse); 536 } else if (pointer_x < x_off + follow_mouse) 537 x_off -= (x_off + follow_mouse) - pointer_x; 538 if (pointer_y > y_off + s->height - follow_mouse) { 539 y_off += pointer_y - (y_off + s->height - follow_mouse); 540 } else if (pointer_y < y_off + follow_mouse) 541 y_off -= (y_off + follow_mouse) - pointer_y; 542 } 543 // adjust grabbing region position if it goes out of screen. 544 s->x_off = x_off = FFMIN(FFMAX(x_off, 0), screen_w - s->width); 545 s->y_off = y_off = FFMIN(FFMAX(y_off, 0), screen_h - s->height); 546 547 if (s->show_region && s->region_win) 548 XMoveWindow(dpy, s->region_win, 549 s->x_off - REGION_WIN_BORDER, 550 s->y_off - REGION_WIN_BORDER); 551 } 552 553 if (s->show_region) { 554 if (s->region_win) { 555 XEvent evt; 556 // clean up the events, and do the initinal draw or redraw. 557 for (evt.type = NoEventMask; XCheckMaskEvent(dpy, ExposureMask | StructureNotifyMask, &evt); ); 558 if (evt.type) 559 x11grab_draw_region_win(s); 560 } else { 561 x11grab_region_win_init(s); 562 } 563 } 564 565 if(s->use_shm) { 566 if (!XShmGetImage(dpy, root, image, x_off, y_off, AllPlanes)) { 567 av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n"); 568 } 569 } else { 570 if (!xget_zpixmap(dpy, root, image, x_off, y_off)) { 571 av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n"); 572 } 573 } 574 575 if (s->draw_mouse) { 576 paint_mouse_pointer(image, s); 577 } 578 579 return s->frame_size; 580} 581 582/** 583 * Close x11 frame grabber (public device demuxer API). 584 * 585 * @param s1 Context from avformat core 586 * @return 0 success, !0 failure 587 */ 588static int 589x11grab_read_close(AVFormatContext *s1) 590{ 591 struct x11grab *x11grab = s1->priv_data; 592 593 /* Detach cleanly from shared mem */ 594 if (x11grab->use_shm) { 595 XShmDetach(x11grab->dpy, &x11grab->shminfo); 596 shmdt(x11grab->shminfo.shmaddr); 597 shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL); 598 } 599 600 /* Destroy X11 image */ 601 if (x11grab->image) { 602 XDestroyImage(x11grab->image); 603 x11grab->image = NULL; 604 } 605 606 if (x11grab->region_win) { 607 XDestroyWindow(x11grab->dpy, x11grab->region_win); 608 } 609 610 /* Free X11 display */ 611 XCloseDisplay(x11grab->dpy); 612 return 0; 613} 614 615#define OFFSET(x) offsetof(struct x11grab, x) 616#define DEC AV_OPT_FLAG_DECODING_PARAM 617static const AVOption options[] = { 618 { "draw_mouse", "draw the mouse pointer", OFFSET(draw_mouse), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, DEC }, 619 620 { "follow_mouse", "move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region", 621 OFFSET(follow_mouse), AV_OPT_TYPE_INT, {.i64 = 0}, -1, INT_MAX, DEC, "follow_mouse" }, 622 { "centered", "keep the mouse pointer at the center of grabbing region when following", 623 0, AV_OPT_TYPE_CONST, {.i64 = -1}, INT_MIN, INT_MAX, DEC, "follow_mouse" }, 624 625 { "framerate", "set video frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "ntsc"}, 0, 0, DEC }, 626 { "show_region", "show the grabbing region", OFFSET(show_region), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC }, 627 { "video_size", "set video frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = "vga"}, 0, 0, DEC }, 628 { NULL }, 629}; 630 631static const AVClass x11_class = { 632 .class_name = "X11grab indev", 633 .item_name = av_default_item_name, 634 .option = options, 635 .version = LIBAVUTIL_VERSION_INT, 636 .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, 637}; 638 639/** x11 grabber device demuxer declaration */ 640AVInputFormat ff_x11grab_demuxer = { 641 .name = "x11grab", 642 .long_name = NULL_IF_CONFIG_SMALL("X11grab"), 643 .priv_data_size = sizeof(struct x11grab), 644 .read_header = x11grab_read_header, 645 .read_packet = x11grab_read_packet, 646 .read_close = x11grab_read_close, 647 .flags = AVFMT_NOFILE, 648 .priv_class = &x11_class, 649}; 650