28#if CONFIG_LIBXCB_XFIXES
29#include <xcb/xfixes.h>
37#if CONFIG_LIBXCB_SHAPE
83#define FOLLOW_CENTER -1
85#define OFFSET(x) offsetof(XCBGrabContext, x)
86#define D AV_OPT_FLAG_DECODING_PARAM
88 {
"window_id",
"Window to capture.",
OFFSET(window_id),
AV_OPT_TYPE_INT, { .i64 = XCB_NONE }, 0, UINT32_MAX,
D },
96 {
"follow_mouse",
"Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
98 {
"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, .unit =
"follow_mouse" },
99 {
"show_region",
"Show the grabbing region.",
OFFSET(show_region),
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1,
D },
100 {
"region_border",
"Set the region border thickness.",
OFFSET(region_border),
AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 128,
D },
106 .class_name =
"xcbgrab indev",
114 xcb_query_pointer_reply_t *p,
115 xcb_get_geometry_reply_t *geo)
118 int x =
c->x, y =
c->y;
119 int w =
c->width,
h =
c->height,
f =
c->follow_mouse;
133 int right = x +
w -
f;
135 int bottom = y +
h -
f;
138 }
else if (p_x <
left) {
143 }
else if (p_y < top) {
162 xcb_get_image_cookie_t iq;
163 xcb_get_image_reply_t *
img;
164 xcb_drawable_t drawable =
c->window_id;
165 xcb_generic_error_t *e =
NULL;
169 iq = xcb_get_image(
c->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable,
170 c->x,
c->y,
c->width,
c->height, ~0);
172 img = xcb_get_image_reply(
c->conn, iq, &e);
176 "Cannot get the image data "
177 "event_error: response_type:%u error_code:%u "
178 "sequence:%u resource_id:%u minor_code:%u major_code:%u.\n",
179 e->response_type, e->error_code,
180 e->sequence, e->resource_id, e->minor_code, e->major_code);
188 data = xcb_get_image_data(
img);
189 length = xcb_get_image_data_length(
img);
208 c->time_frame +=
c->frame_duration;
212 delay =
c->time_frame - curtime;
222static int check_shm(xcb_connection_t *conn)
224 xcb_shm_query_version_cookie_t cookie = xcb_shm_query_version(conn);
225 xcb_shm_query_version_reply_t *reply;
227 reply = xcb_shm_query_version_reply(conn, cookie,
NULL);
236static void free_shm_buffer(
void *opaque, uint8_t *
data)
243 xcb_connection_t *conn = opaque;
249 id = shmget(IPC_PRIVATE,
size, IPC_CREAT | 0777);
253 segment = xcb_generate_id(conn);
254 xcb_shm_attach(conn,
segment,
id, 0);
256 shmctl(
id, IPC_RMID, 0);
270 xcb_shm_get_image_cookie_t iq;
271 xcb_shm_get_image_reply_t *
img;
272 xcb_drawable_t drawable =
c->window_id;
273 xcb_generic_error_t *e =
NULL;
284 iq = xcb_shm_get_image(
c->conn, drawable,
285 c->x,
c->y,
c->width,
c->height, ~0,
286 XCB_IMAGE_FORMAT_Z_PIXMAP,
segment, 0);
287 img = xcb_shm_get_image_reply(
c->conn, iq, &e);
293 "Cannot get the image data "
294 "event_error: response_type:%u error_code:%u "
295 "sequence:%u resource_id:%u minor_code:%u major_code:%u.\n",
296 e->response_type, e->error_code,
297 e->sequence, e->resource_id, e->minor_code, e->major_code);
308 pkt->size =
c->frame_size;
314#if CONFIG_LIBXCB_XFIXES
315static int check_xfixes(xcb_connection_t *conn)
317 xcb_xfixes_query_version_cookie_t cookie;
318 xcb_xfixes_query_version_reply_t *reply;
320 cookie = xcb_xfixes_query_version(conn, XCB_XFIXES_MAJOR_VERSION,
321 XCB_XFIXES_MINOR_VERSION);
322 reply = xcb_xfixes_query_version_reply(conn, cookie,
NULL);
331#define BLEND(target, source, alpha) \
332 (target) + ((source) * (255 - (alpha)) + 255 / 2) / 255
335 xcb_query_pointer_reply_t *p,
336 xcb_get_geometry_reply_t *geo,
337 int win_x,
int win_y)
341 uint8_t *image =
pkt->data;
343 xcb_xfixes_get_cursor_image_cookie_t cc;
344 xcb_xfixes_get_cursor_image_reply_t *ci;
345 int cx, cy, x, y,
w,
h, c_off, i_off;
347 cc = xcb_xfixes_get_cursor_image(gr->
conn);
348 ci = xcb_xfixes_get_cursor_image_reply(gr->
conn, cc,
NULL);
352 cursor = xcb_xfixes_get_cursor_image_cursor_image(ci);
356 cx = ci->x - ci->xhot;
357 cy = ci->y - ci->yhot;
359 x =
FFMAX(cx, win_x + gr->
x);
360 y =
FFMAX(cy, win_y + gr->
y);
362 w =
FFMIN(cx + ci->width, win_x + gr->
x + gr->
width) - x;
366 i_off = x - gr->
x - win_x;
368 cursor += (y - cy) * ci->width;
371 for (y = 0; y <
h; y++) {
374 for (x = 0; x <
w; x++, cursor++, image +=
stride) {
378 g = (*cursor >> 8) & 0xff;
379 b = (*cursor >> 16) & 0xff;
380 a = (*cursor >> 24) & 0xff;
390 image[0] = BLEND(
r, image[0],
a);
391 image[1] = BLEND(
g, image[1],
a);
392 image[2] = BLEND(
b, image[2],
a);
396 cursor += ci->width -
w - c_off;
407 const uint32_t args[] = { win_x +
c->x -
c->region_border,
408 win_y +
c->y -
c->region_border };
410 xcb_configure_window(
c->conn,
412 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
419 xcb_query_pointer_cookie_t pc;
420 xcb_get_geometry_cookie_t gc;
421 xcb_translate_coordinates_cookie_t tc;
422 xcb_query_pointer_reply_t *p =
NULL;
423 xcb_get_geometry_reply_t *geo =
NULL;
424 xcb_translate_coordinates_reply_t *translate =
NULL;
427 int win_x = 0, win_y = 0;
432 if (
c->follow_mouse ||
c->draw_mouse) {
433 pc = xcb_query_pointer(
c->conn,
c->window_id);
434 gc = xcb_get_geometry(
c->conn,
c->window_id);
435 p = xcb_query_pointer_reply(
c->conn, pc,
NULL);
440 geo = xcb_get_geometry_reply(
c->conn, gc,
NULL);
447 if (
c->window_id !=
c->screen->root) {
448 tc = xcb_translate_coordinates(
c->conn,
c->window_id,
c->screen->root, 0, 0);
449 translate = xcb_translate_coordinates_reply(
c->conn, tc,
NULL);
456 win_x = translate->dst_x;
457 win_y = translate->dst_y;
461 if (
c->follow_mouse && p->same_screen)
468 if (
c->has_shm && xcbgrab_frame_shm(
s,
pkt) < 0) {
476 pkt->duration =
c->frame_duration;
478#if CONFIG_LIBXCB_XFIXES
479 if (ret >= 0 &&
c->draw_mouse && p->same_screen)
480 xcbgrab_draw_mouse(
s,
pkt, p, geo, win_x, win_y);
497 xcb_disconnect(
ctx->conn);
502static xcb_screen_t *
get_screen(
const xcb_setup_t *setup,
int screen_num)
504 xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup);
505 xcb_screen_t *screen =
NULL;
507 for (; it.rem > 0; xcb_screen_next (&it)) {
523 const xcb_setup_t *setup = xcb_get_setup(
c->conn);
524 const xcb_format_t *fmt = xcb_setup_pixmap_formats(setup);
525 int length = xcb_setup_pixmap_formats_length(setup);
530 if (fmt->depth == depth) {
533 if (fmt->bits_per_pixel == 32)
534 *
pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
538 if (fmt->bits_per_pixel == 32)
539 *
pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
541 else if (fmt->bits_per_pixel == 24)
542 *
pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
546 if (fmt->bits_per_pixel == 16)
547 *
pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
551 if (fmt->bits_per_pixel == 16)
552 *
pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
556 if (fmt->bits_per_pixel == 8)
563 *bpp = fmt->bits_per_pixel;
578 xcb_get_geometry_cookie_t gc;
579 xcb_get_geometry_reply_t *geo;
592 gc = xcb_get_geometry(
c->conn,
c->window_id);
593 geo = xcb_get_geometry_reply(
c->conn, gc,
NULL);
599 if (!
c->width || !
c->height) {
600 c->width = geo->width;
601 c->height = geo->height;
604 if (
c->x +
c->width > geo->width ||
605 c->y +
c->height > geo->height) {
607 "Capture area %dx%d at position %d.%d "
608 "outside the screen size %dx%d\n",
611 geo->width, geo->height);
626 frame_size_bits = (
int64_t)
c->width *
c->height *
c->bpp;
631 c->frame_size = frame_size_bits / 8;
635 c->conn, allocate_shm_buffer,
NULL);
652 xcb_gcontext_t gc = xcb_generate_id(
c->conn);
653 uint32_t
mask = XCB_GC_FOREGROUND |
658 uint32_t values[] = {
c->screen->black_pixel,
659 c->screen->white_pixel,
661 XCB_LINE_STYLE_DOUBLE_DASH,
662 XCB_FILL_STYLE_SOLID };
663 xcb_rectangle_t
r = { 1, 1,
664 c->width +
c->region_border * 2 - 3,
665 c->height +
c->region_border * 2 - 3 };
667 xcb_create_gc(
c->conn, gc,
c->window,
mask, values);
669 xcb_poly_rectangle(
c->conn,
c->window, gc, 1, &
r);
675 uint32_t
mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
676 uint32_t values[] = { 1,
677 XCB_EVENT_MASK_EXPOSURE |
678 XCB_EVENT_MASK_STRUCTURE_NOTIFY };
681 c->window = xcb_generate_id(
c->conn);
683 xcb_create_window(
c->conn, XCB_COPY_FROM_PARENT,
686 c->x -
c->region_border,
687 c->y -
c->region_border,
688 c->width +
c->region_border * 2,
689 c->height +
c->region_border * 2,
691 XCB_WINDOW_CLASS_INPUT_OUTPUT,
692 XCB_COPY_FROM_PARENT,
695#if CONFIG_LIBXCB_SHAPE
696 xcb_shape_rectangles(
c->conn, XCB_SHAPE_SO_SUBTRACT,
697 XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED,
699 c->region_border,
c->region_border,
703 xcb_map_window(
c->conn,
c->window);
708#define CROSSHAIR_CURSOR 34
711 xcb_point_t *corner_b)
713 xcb_rectangle_t rectangle;
714 rectangle.x =
FFMIN(corner_a->x, corner_b->x);
715 rectangle.y =
FFMIN(corner_a->y, corner_b->y);
716 rectangle.width =
FFABS(corner_a->x - corner_b->x);
717 rectangle.height =
FFABS(corner_a->y - corner_b->y);
724 xcb_connection_t *conn =
c->conn;
725 xcb_screen_t *screen =
c->screen;
727 int ret = 0, done = 0, was_pressed = 0;
729 xcb_font_t cursor_font;
730 xcb_point_t press_position;
731 xcb_generic_event_t *event;
732 xcb_rectangle_t rectangle = { 0 };
733 xcb_grab_pointer_reply_t *reply;
734 xcb_grab_pointer_cookie_t cookie;
736 xcb_window_t root_window = screen->root;
737 xcb_gcontext_t gc = xcb_generate_id(conn);
738 uint32_t
mask = XCB_GC_FUNCTION | XCB_GC_SUBWINDOW_MODE;
739 uint32_t values[] = { XCB_GX_INVERT, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS };
740 xcb_create_gc(conn, gc, root_window,
mask, values);
742 cursor_font = xcb_generate_id(conn);
743 xcb_open_font(conn, cursor_font, strlen(
"cursor"),
"cursor");
744 cursor = xcb_generate_id(conn);
745 xcb_create_glyph_cursor(conn, cursor, cursor_font, cursor_font,
747 0xFFFF, 0xFFFF, 0xFFFF);
748 cookie = xcb_grab_pointer(conn, 0, root_window,
749 XCB_EVENT_MASK_BUTTON_PRESS |
750 XCB_EVENT_MASK_BUTTON_RELEASE |
751 XCB_EVENT_MASK_BUTTON_MOTION,
752 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
753 root_window, cursor, XCB_CURRENT_TIME);
754 reply = xcb_grab_pointer_reply(conn, cookie,
NULL);
755 if (!reply || reply->status != XCB_GRAB_STATUS_SUCCESS) {
757 "Failed to select region. Could not grab pointer.\n");
764 xcb_grab_server(conn);
766 while (!done && (event = xcb_wait_for_event(conn))) {
767 switch (event->response_type & ~0x80) {
768 case XCB_BUTTON_PRESS: {
769 xcb_button_press_event_t *press = (xcb_button_press_event_t *)event;
770 press_position = (xcb_point_t){ press->event_x, press->event_y };
771 rectangle.x = press_position.x;
772 rectangle.y = press_position.y;
773 xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle);
777 case XCB_MOTION_NOTIFY: {
779 xcb_motion_notify_event_t *motion =
780 (xcb_motion_notify_event_t *)event;
781 xcb_point_t cursor_position = { motion->event_x, motion->event_y };
782 xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle);
784 xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle);
788 case XCB_BUTTON_RELEASE: {
789 xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle);
799 c->width = rectangle.width;
800 c->height = rectangle.height;
801 if (
c->width &&
c->height) {
808 xcb_ungrab_server(conn);
809 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
813 xcb_free_cursor(conn, cursor);
814 xcb_close_font(conn, cursor_font);
815 xcb_free_gc(conn, gc);
823 const xcb_setup_t *setup;
829 if (!sscanf(
s->url,
"%[^+]+%d,%d", display_name, &
c->x, &
c->y)) {
831 if(sscanf(
s->url,
"+%d,%d", &
c->x, &
c->y) != 2) {
837 c->conn = xcb_connect(display_name[0] ? display_name :
NULL, &screen_num);
840 if ((ret = xcb_connection_has_error(
c->conn))) {
842 s->url[0] ?
s->url :
"default", ret);
846 setup = xcb_get_setup(
c->conn);
856 if (
c->window_id == XCB_NONE)
857 c->window_id =
c->screen->root;
859 if (
c->select_region) {
861 c->select_region = 0;
863 if (
c->follow_mouse) {
869 if (
c->select_region) {
885 c->has_shm = check_shm(
c->conn);
888#if CONFIG_LIBXCB_XFIXES
890 if (!(
c->draw_mouse = check_xfixes(
c->conn))) {
892 "XFixes not available, cannot draw the mouse.\n");
const FFInputFormat ff_xcbgrab_demuxer
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
static int BS_FUNC left(const BSCTX *bc)
Return the number of the bits left in a buffer.
#define FFABS(a)
Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they are not representable ...
static enum AVPixelFormat pix_fmt
static int read_header(FFV1Context *f, RangeCoder *c)
@ AV_OPT_TYPE_IMAGE_SIZE
Underlying C type is two consecutive integers.
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
@ AV_OPT_TYPE_INT
Underlying C type is int.
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
#define AV_INPUT_BUFFER_PADDING_SIZE
Required number of additionally allocated bytes at the end of the input bitstream for decoding.
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
AVBufferRef * av_buffer_create(uint8_t *data, size_t size, void(*free)(void *opaque, uint8_t *data), void *opaque, int flags)
Create an AVBuffer from an existing array.
void * av_buffer_pool_buffer_get_opaque(const AVBufferRef *ref)
Query the original opaque parameter of an allocated buffer in the pool.
void av_buffer_pool_uninit(AVBufferPool **ppool)
Mark the pool as being available for freeing.
AVBufferRef * av_buffer_pool_get(AVBufferPool *pool)
Allocate a new AVBuffer, reusing an old buffer from the pool when available.
AVBufferPool * av_buffer_pool_init2(size_t size, void *opaque, AVBufferRef *(*alloc)(void *opaque, size_t size), void(*pool_free)(void *opaque))
Allocate and initialize a buffer pool with a more complex allocator.
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
#define AVERROR_EXTERNAL
Generic error in an external library.
#define AV_LOG_WARNING
Something somehow does not look correct.
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
const char * av_default_item_name(void *ptr)
Return the context name.
int64_t av_rescale(int64_t a, int64_t b, int64_t c)
Rescale a 64-bit integer with rounding to nearest.
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
Rescale a 64-bit integer by 2 rational numbers.
#define AV_TIME_BASE_Q
Internal time base represented as fractional value.
#define LIBAVUTIL_VERSION_INT
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
void avpriv_report_missing_feature(void *avc, const char *msg,...) av_printf_format(2
Log a generic warning message about a missing feature.
static av_cold int read_close(AVFormatContext *ctx)
@ AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT
static const uint16_t mask[17]
Memory handling functions.
int av_parse_video_rate(AVRational *rate, const char *arg)
Parse str and store the detected values in *rate.
@ AV_PIX_FMT_RGB24
packed RGB 8:8:8, 24bpp, RGBRGB...
@ AV_PIX_FMT_BGR0
packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined
@ AV_PIX_FMT_RGB555BE
packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), big-endian , X=unused/undefined
@ AV_PIX_FMT_RGB8
packed RGB 3:3:2, 8bpp, (msb)3R 3G 2B(lsb)
@ AV_PIX_FMT_RGB565LE
packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), little-endian
@ AV_PIX_FMT_RGB555LE
packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), little-endian, X=unused/undefined
@ AV_PIX_FMT_RGB565BE
packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), big-endian
@ AV_PIX_FMT_BGR24
packed RGB 8:8:8, 24bpp, BGRBGR...
@ AV_PIX_FMT_0RGB
packed RGB 8:8:8, 32bpp, XRGBXRGB... X=unused/undefined
A reference to a data buffer.
uint8_t * data
The data buffer.
Describe the class of an AVClass context structure.
int height
The height of the video frame in pixels.
int width
The width of the video frame in pixels.
int64_t bit_rate
The average bitrate of the encoded data (in bits per second).
enum AVMediaType codec_type
General type of the encoded data.
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
This structure stores compressed data.
Rational number (pair of numerator and denominator).
AVCodecParameters * codecpar
Codec parameters associated with this stream.
AVRational avg_frame_rate
Average framerate.
static int ref[MAX_W *MAX_W]
static AVFormatContext * ctx
int av_usleep(unsigned usec)
Sleep for a period of time.
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
int64_t av_gettime(void)
Get the current time in microseconds.
static xcb_rectangle_t rectangle_from_corners(xcb_point_t *corner_a, xcb_point_t *corner_b)
static int64_t wait_frame(AVFormatContext *s, AVPacket *pkt)
static void xcbgrab_image_reply_free(void *opaque, uint8_t *data)
static int select_region(AVFormatContext *s)
static int xcbgrab_reposition(AVFormatContext *s, xcb_query_pointer_reply_t *p, xcb_get_geometry_reply_t *geo)
static int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt)
static void setup_window(AVFormatContext *s)
static void xcbgrab_update_region(AVFormatContext *s, int win_x, int win_y)
static void draw_rectangle(AVFormatContext *s)
static const AVClass xcbgrab_class
static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt)
static int pixfmt_from_pixmap_format(AVFormatContext *s, int depth, int *pix_fmt, int *bpp)
static int create_stream(AVFormatContext *s)
static av_cold int xcbgrab_read_header(AVFormatContext *s)
static av_cold int xcbgrab_read_close(AVFormatContext *s)
static xcb_screen_t * get_screen(const xcb_setup_t *setup, int screen_num)