From patchwork Tue Nov 8 02:57:22 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Radom=C3=ADr_Pol=C3=A1ch?= X-Patchwork-Id: 1346 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.90.1 with SMTP id o1csp1905992vsb; Tue, 8 Nov 2016 13:08:08 -0800 (PST) X-Received: by 10.194.155.35 with SMTP id vt3mr11809967wjb.223.1478639288068; Tue, 08 Nov 2016 13:08:08 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id nc5si33352131wjb.223.2016.11.08.13.08.07; Tue, 08 Nov 2016 13:08:08 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CA7EA689AE5; Tue, 8 Nov 2016 23:07:51 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from kni.int.shy.cz (ip-89-177-120-60.net.upcbroadband.cz [89.177.120.60]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 67B06689BB3 for ; Tue, 8 Nov 2016 04:57:23 +0200 (EET) Received: from kni.int.shy.cz (localhost [127.0.0.1]) by kni.int.shy.cz (8.15.2/8.15.2/Debian-3) with ESMTP id uA82vQ43014854; Tue, 8 Nov 2016 03:57:26 +0100 Received: (from polach@localhost) by kni.int.shy.cz (8.15.2/8.15.2/Submit) id uA82vQ8O014853; Tue, 8 Nov 2016 03:57:26 +0100 From: =?UTF-8?q?Ing=2E=20Radom=C3=ADr=20Pol=C3=A1ch?= To: ffmpeg-devel@ffmpeg.org Date: Tue, 8 Nov 2016 03:57:22 +0100 Message-Id: <1478573842-14809-1-git-send-email-rp@t4d.cz> X-Mailer: git-send-email 2.7.4 MIME-Version: 1.0 X-Mailman-Approved-At: Tue, 08 Nov 2016 23:07:50 +0200 Subject: [FFmpeg-devel] [PATCH] feature: xcbgrab single window X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: =?UTF-8?q?Ing=2E=20Radom=C3=ADr=20Pol=C3=A1ch?= Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Allows to grab only a single window by using syntax: ffmpeg -f x11grab -r 15 -i ":0/0x0580001b/parent2" ... The syntax for input is "display/grab_window_id/focus_window_id". If focus_window_id is omitted it is set as grab_window_id. There are special constants for focus_window_id: - this: grab_window_id - parent: parent window id of grab_window, - parent2: grand parent window id of grab_window, - parent3: great grand parent window id of grab_window. Has a single command line option repeat_frame which controls out of focus streaming. Turned on in default for repeating the last frame. If turned off sends empty (zero length) packet to the ffmpeg backend. --- Changelog | 1 + libavdevice/xcbgrab.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 201 insertions(+), 10 deletions(-) diff --git a/Changelog b/Changelog index 11a769a..eb83365 100644 --- a/Changelog +++ b/Changelog @@ -3,6 +3,7 @@ releases are sorted from youngest to oldest. version : - CrystalHD decoder moved to new decode API +- XCB-based screen-grabbing of single window version 3.2: - libopenmpt demuxer diff --git a/libavdevice/xcbgrab.c b/libavdevice/xcbgrab.c index 702e66c..a27cdee 100644 --- a/libavdevice/xcbgrab.c +++ b/libavdevice/xcbgrab.c @@ -1,6 +1,8 @@ /* * XCB input grabber * Copyright (C) 2014 Luca Barbato + * Copyright (C) 2016 Radomír Polách + * Copyright (C) 2016 Kristýna Dudová * * This file is part of FFmpeg. * @@ -70,11 +72,21 @@ typedef struct XCBGrabContext { int show_region; int region_border; int centered; + int repeat_frame; const char *video_size; const char *framerate; int has_shm; + + char *focus_name; + char *grab_name; + xcb_window_t focus_window; + xcb_window_t grab_window; + + uint8_t *data; + int size; + int warned; } XCBGrabContext; #define FOLLOW_CENTER -1 @@ -88,6 +100,7 @@ static const AVOption options[] = { { "grab_y", "Initial y coordinate.", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = "vga" }, 0, 0, D }, { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "ntsc" }, 0, 0, D }, + { "repeat_frame", "Repeat last frame, when window is not active or data are not available.", OFFSET(repeat_frame), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, D }, { "draw_mouse", "Draw the mouse pointer.", OFFSET(draw_mouse), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, D }, { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.", OFFSET(follow_mouse), AV_OPT_TYPE_INT, { .i64 = 0 }, FOLLOW_CENTER, INT_MAX, D, "follow_mouse" }, @@ -156,9 +169,13 @@ static int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt) uint8_t *data; int length, ret; - iq = xcb_get_image(c->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable, - c->x, c->y, c->width, c->height, ~0); - + if (c->focus_name) { + iq = xcb_get_image(c->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, c->grab_window, + 0, 0, c->width, c->height, ~0); + } else { + iq = xcb_get_image(c->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable, + c->x, c->y, c->width, c->height, ~0); + } img = xcb_get_image_reply(c->conn, iq, &e); if (e) { @@ -261,9 +278,16 @@ static int xcbgrab_frame_shm(AVFormatContext *s, AVPacket *pkt) if (ret < 0) return ret; - iq = xcb_shm_get_image(c->conn, drawable, - c->x, c->y, c->width, c->height, ~0, - XCB_IMAGE_FORMAT_Z_PIXMAP, c->segment, 0); + if (c->focus_name) { + iq = xcb_shm_get_image(c->conn, c->grab_window, + 0, 0, c->width, c->height, ~0, + XCB_IMAGE_FORMAT_Z_PIXMAP, c->segment, 0); + } else { + iq = xcb_shm_get_image(c->conn, drawable, + c->x, c->y, c->width, c->height, ~0, + XCB_IMAGE_FORMAT_Z_PIXMAP, c->segment, 0); + } + img = xcb_shm_get_image_reply(c->conn, iq, &e); xcb_flush(c->conn); @@ -329,8 +353,13 @@ static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt, if (!cursor) return; - cx = ci->x - ci->xhot; - cy = ci->y - ci->yhot; + if (gr->focus_name) { + cx = p->win_x - ci->xhot; + cy = p->win_y - ci->yhot; + } else { + cx = ci->x - ci->xhot; + cy = ci->y - ci->yhot; + } x = FFMAX(cx, gr->x); y = FFMAX(cy, gr->y); @@ -389,6 +418,68 @@ static void xcbgrab_update_region(AVFormatContext *s) args); } +static xcb_window_t get_window_focus(AVFormatContext *s) +{ + XCBGrabContext *ctx = s->priv_data; + xcb_window_t w = 0; + xcb_get_input_focus_cookie_t c; + xcb_get_input_focus_reply_t *r; + + c = xcb_get_input_focus(ctx->conn); + r = xcb_get_input_focus_reply(ctx->conn, c, NULL); + if (!r) + return -1; + + w = r->focus; + free(r); + return w; +} + +static xcb_window_t get_window_parent(AVFormatContext *s, xcb_window_t w) +{ + XCBGrabContext *ctx = s->priv_data; + xcb_query_tree_cookie_t c; + xcb_query_tree_reply_t *r; + xcb_generic_error_t *e; + + c = xcb_query_tree(ctx->conn, w); + r = xcb_query_tree_reply(ctx->conn, c, &e); + if (!r) + return -1; + + w = r->parent; + free(r); + return w; +} + +static void xcbgrab_store_packet(AVFormatContext *s, AVPacket *pkt) { + XCBGrabContext *c = s->priv_data; + + c->size = pkt->size; + + if (c->size) { + if (!c->data) { + c->data = av_malloc(c->size); + } + memcpy(c->data, pkt->data, c->size); + } +} + +static int xcbgrab_load_packet(AVFormatContext *s, AVPacket *pkt) { + XCBGrabContext *c = s->priv_data; + int ret; + + if (!c->size) + return 0; + + ret = av_new_packet(pkt, c->size); + + if (!ret) + memcpy(pkt->data, c->data, c->size); + + return ret; +} + static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) { XCBGrabContext *c = s->priv_data; @@ -400,11 +491,56 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) wait_frame(s, pkt); + if (c->focus_name) { + xcb_window_t w = get_window_focus(s); + if (w != c->focus_window) { + if (!c->warned) { + c->warned = 1; + av_log(s, AV_LOG_WARNING, + "Not grabbing, focus window not focused, focused window is 0x%08x.\n", w); + } + if (c->repeat_frame) { + return xcbgrab_load_packet(s, pkt); + } else { + return 0; + } + } else { + if (c->warned == 1) { + c->warned = 0; + av_log(s, AV_LOG_WARNING, + "Grabbing resumed.\n"); + } + } + } + if (c->follow_mouse || c->draw_mouse) { - pc = xcb_query_pointer(c->conn, c->screen->root); - gc = xcb_get_geometry(c->conn, c->screen->root); + if (c->focus_name) { + pc = xcb_query_pointer(c->conn, c->grab_window); + gc = xcb_get_geometry(c->conn, c->grab_window); + } else { + pc = xcb_query_pointer(c->conn, c->screen->root); + gc = xcb_get_geometry(c->conn, c->screen->root); + } p = xcb_query_pointer_reply(c->conn, pc, NULL); geo = xcb_get_geometry_reply(c->conn, gc, NULL); + if (geo->width < c->width || geo->height < c->height) { + if (!c->warned) { + c->warned = 2; + av_log(s, AV_LOG_WARNING, + "Not grabbing, grab window width or height lower than initial.\n"); + } + if (c->repeat_frame) { + return xcbgrab_load_packet(s, pkt); + } else { + return 0; + } + } else { + if (c->warned == 2) { + c->warned = 0; + av_log(s, AV_LOG_WARNING, + "Grabbing resumed.\n"); + } + } } if (c->follow_mouse && p->same_screen) @@ -425,6 +561,10 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) xcbgrab_draw_mouse(s, pkt, p, geo); #endif + if (c->repeat_frame) { + xcbgrab_store_packet(s, pkt); + } + free(p); free(geo); @@ -443,6 +583,10 @@ static av_cold int xcbgrab_read_close(AVFormatContext *s) xcb_disconnect(ctx->conn); + if (ctx->data) { + free(ctx->data); + } + return 0; } @@ -535,6 +679,19 @@ static int create_stream(AVFormatContext *s) avpriv_set_pts_info(st, 64, 1, 1000000); + if (c->focus_name) { + gc = xcb_get_geometry(c->conn, c->grab_window); + geo = xcb_get_geometry_reply(c->conn, gc, NULL); + if (!geo) { + av_log(s, AV_LOG_ERROR, + "Grab window 0x%08x does not exist.\n", + c->grab_window); + return AVERROR(EINVAL); + } + c->width = geo->width & ~1; + c->height = geo->height & ~1; + } + gc = xcb_get_geometry(c->conn, c->screen->root); geo = xcb_get_geometry_reply(c->conn, gc, NULL); @@ -630,6 +787,17 @@ static av_cold int xcbgrab_read_header(AVFormatContext *s) int screen_num, ret; const xcb_setup_t *setup; char *display_name = av_strdup(s->filename); + if (c->focus_name = strchr(s->filename, '/')) { + c->focus_name[0] = '\0'; + ++c->focus_name; + } + if (c->grab_name = strchr(c->focus_name, '/')) { + c->grab_name[0] = '\0'; + ++c->grab_name; + } + c->data = NULL; + c->size = 0; + c->warned = 0; if (!display_name) return AVERROR(ENOMEM); @@ -658,6 +826,28 @@ static av_cold int xcbgrab_read_header(AVFormatContext *s) return AVERROR(EIO); } + if (c->focus_name) { + c->focus_window = strtoul(c->focus_name, NULL, 16); + if (c->grab_name) { + if (!strcmp(c->grab_name,"this")) { + c->grab_window = c->focus_window; + } else if (!strcmp(c->grab_name,"parent")) { + c->grab_window = get_window_parent(s, c->focus_window); + } else if (!strcmp(c->grab_name,"parent2")) { + c->grab_window = get_window_parent(s, c->focus_window); + c->grab_window = get_window_parent(s, c->grab_window); + } else if (!strcmp(c->grab_name,"parent3")) { + c->grab_window = get_window_parent(s, c->focus_window); + c->grab_window = get_window_parent(s, c->grab_window); + c->grab_window = get_window_parent(s, c->grab_window); + } else { + c->grab_window = strtoul(c->grab_name, NULL, 16); + } + } else { + c->grab_window = get_window_parent(s, c->focus_window); + } + } + ret = create_stream(s); if (ret < 0) {