From patchwork Mon Feb 22 16:53:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: sgerwk-at-aol.com@ffmpeg.org X-Patchwork-Id: 25904 Delivered-To: andriy.gelman@gmail.com Received: by 2002:a25:6d42:0:0:0:0:0 with SMTP id i63csp1808874ybc; Mon, 22 Feb 2021 08:54:01 -0800 (PST) X-Google-Smtp-Source: ABdhPJxr9ZmRkM5opxexUCjXU/B3kUx7vNOxeZ3ma4nsA+GK9jNwu+n4cgmOeGuaEEkiTYnZo0vW X-Received: by 2002:a17:906:c09:: with SMTP id s9mr21669491ejf.539.1614012841630; Mon, 22 Feb 2021 08:54:01 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1614012841; cv=none; d=google.com; s=arc-20160816; b=nHqCAm3jqsWb/sLxV+WXAUYfrj+tYbu1/QLpUzR2hnI4Qi4myK5I+wlNex2ezZWRz9 EohRuKXirrk4YTwoyovRSbwqXcKlzQlNnfZQdgFHZM255F1AEdfFKq2hIvC8kYT+FdFi /hrAnoeYVLf7sufQefJML105Kpe8b3kQ8o/XN5lWFSmOpuCxcqGUlIR7FXBiR3ZFwunH fElAJu1jmS/0w4lNvfTazDYsMPJRlEhVS7b3m+lODyUpGGTqOdhV308zdDHCDf6L516V p76rvjYE0QmHjplJ4NMaKjOXvwtYrY2ku6Yadn6nkluE7Z4QlfCE8IS4qoo/HZHUVvrb 0aqw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:message-id:in-reply-to:to:from:date :dkim-signature:delivered-to; bh=0C7cV+8KHRQpMUIJi8xs/PrPA1DFgBpzNbBORyuimd4=; b=fkMyTetwVNqUiae8IGIRpAMk+aaixfwvTvsXJ+FeXD71MzzRfRycP8Nk0b2GsVDXs1 gyS4MU3hXnhhP6ysJTuwSKd/lQWli42geTJpAERWL8hA/ey0N48yJc0uKIS9BfmJ7NPW o9T0tb1VqJcqhjl2MVbz1LH3qLezerSNxnvmv33OZmdb7Uf6EMnURg+ZZOxVZMso8Nay m4gQteum0fitc1VLMopeNRWrwGTM9GerzeXo19iUhNdPEEtCHC8mNYO6jEkcfNQCqKji UGe6ec++eBWLa99Lavb1My0W9wvmE+Al0iOGhG7Dpez6zYCHAbWxCBEaLPQmT1syZ5Qr cDmw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@aol.com header.s=a2048 header.b=jyD5yUKS; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id d5si11637647eja.499.2021.02.22.08.54.01; Mon, 22 Feb 2021 08:54:01 -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; dkim=neutral (body hash did not verify) header.i=@aol.com header.s=a2048 header.b=jyD5yUKS; 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 E708E68AA89; Mon, 22 Feb 2021 18:53:57 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from sonic304-9.consmr.mail.bf2.yahoo.com (sonic304-9.consmr.mail.bf2.yahoo.com [74.6.128.32]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 858B468A912 for ; Mon, 22 Feb 2021 18:53:51 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aol.com; s=a2048; t=1614012830; bh=MdGa8SZH1uvVjkS9ATRPI+DOK6lomVOrRTfbHMIAO6g=; h=Date:From:To:Subject:In-Reply-To:References:From:Subject:Reply-To; b=jyD5yUKStPySqvFEp3+ujTJH74+9UPQBhc6IafWsefjotpKd/k7es8ILyLg1Zc5D88ciQJGHYz/56MGB6ClTjJ250n1NKP/1aZMApUMDrQuUF/lQO2DVD/v6tVW6d9UF70fHtIIZ6CF4Omyk20vLtX65Q4EssuBp/hf5AhmvfE5uRH6ZJDG+Q1IeHXnMKIrfNYF2m4PY/1ZGcvskDz17nV0ggQlBLmkr0AiTjjXciQK55CMF48K5+PCktqLpYbD1Wl8EX6WPXawURJvr21K2LuGMhBWu65GOmxX4aawZrrrXZyjDI8JIM4Pdh4olLKpf2YxNH1n97o3CCp+SJK6TrA== X-SONIC-DKIM-SIGN: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1614012830; bh=5BQ0UM/QYu+In4PwnHwm6tVX4FZ/+B8IJ4n3WWTwFE+=; h=X-Sonic-MF:Date:From:To:Subject:From:Subject; b=UVrpZQ8TreayvVWm0P0iFgJeN9ZD0WkdVxD5smO1bRScUfPgQUXlt3+rNx1P8HBq8uzkIhJtMl292ZRcxpvVgw/IGuMYHHCWEeZYDBD3cnGB/M8aFvGTJycxTdUsoSmwVqLyVI93lyVdZ8r1j5TasrSdslTZOXl88rMyXujQiNro44ch3fm0S0dk+qL+GkoW44dpFdiTJwImEwqgm8ZIypNHbmY6VJuW9iKD/sjbcr62vSbTsIlQpMD/cT7RZtGMBYw7ZUbOVdSnjsU2py9Y/L+w+pe6b4k5KkyIlGVpVrL36sOlAwGncPM27l+ReWMrGw5W9aRZAerRtVkG4sRpBQ== X-YMail-OSG: _EYesKQVM1nQrkIhNCB9mLSLYI5Su2UL9brjVPiMhnGjqwGn6M.kH0TSb2KOes3 YLkku7U4DgtmBUaTpfn2EjKt0yPB6myzuObTEZ4AmUCLu5LaJ0bE1sVY.mua4NUHUfFwqirPV5JY HFwV2HD5v5az.n6X8Pc4Ec9smciumtBHZSZHlZheT4B38l2tm3L95Um51m3v2JrGnjPz0G7F5rce 1F0HDgyo6QqVAACunGQJ3eaJA452ecIuqxHWDPxQJx33kDVjLkE_HHzNTNdtWt6q84I3PwO9_5qs w.LQGczcfanAYhp7hb0MznuGmWeFBQOQR_qZIbNOY0LyJ9zDs1YIORXCdm.5zMz8gPl3KIzTz9qo 576OhJy1Mq7PEbkRMRQ7hxjQoWTgnTpTK4aZOJpHYy1krznygobbIlNfuJjsQduyX6zJDAs2SK9R xuu9K_j3LW0tlX4KebJ1yisaHcuBPDkcOG_7OwlIRvf18UPvadYSMnZ7MD9hM97q494BV4uaJY1u SRsmaEI94.FENP7t7HptScI9OXZNPUB3U5ay7zJS72eJByyqhO4PNslhjyT6vzULvpYdG0QP3d0T HbUzc7eSznJl9eK4YXqsuFD6nJgdVM5Uk5IFpOo_IPoof1dOv7dEjJktuTLMI9_3iTFOIj7KMNhD YVd5UfzWz0mn55O_JMtRFwQFv_3XP_k6th_6YIvYYp2sQ1LuAjT39hRBybhGLIZ7it6js83SBm2p nC2LuoEZuIEsm1v2723cEjadvSnXsQ03R03pCZbMm0421OfOfRusZ7lxHh1fkRwusNFPSYSBOb39 7tO1PSvkW9kjpkXqiwDkJowFq0iH71crq6LLYscJGdhmK9iT.Uehg7HLpCcLWpbvkXcgjyHe499A T7UDZhGcQlXUJkNFK7lYFq0_SRggO3MxkQRmO7JQYcUGw3JGlpfjm0EKEEspLtgp7IYtqXMK8kBY pDJD8lR2YVTy1i7Nn3numA540V7ivVVsUpnc6qhd9Ll5zitAhtdud6eX0nLhgAnazj3HgBh8et17 lRAgUfPaAU5CF0m8OL_HvQcjO6.vkvRuabMYDlpNJBva8f3gIBiBQrcKmVnj2NK_hTwbUx1g8j5L wkE2y07EHE36XpG6wSPw_7eRzNqEVsaQwoMt_UcYULr38T8eKareDhzOWOcFYsbKnOV3dYPu4aqd l2_cPGs_LxSPDxcMbYUOcYNOm17rX2p.UcQpM4vYEaHAOOk2Polnsn_Pe8m_i.cBD1jyDBwo95Tc LUw9lNM9j7CJ6cdcoako_d_d5RbESy6ujudx96IwG0zokYp4LuOvm09DUNpscGDwPwAI3gyqAko_ Wen.gGv8d33BpCroWSnY0fB10SM1Xrm4CSvtCNPgc9O.KkBrVYvoQVLb.Q6YZa1Ys_JAzDcEdWGi wKrNGcr3DpSXz9EWNHf4O5Bp5kMgAxavJtLPaBqZAclCWqB0bBbdMvcia0B2xpL5SkcBjNi8XWrw 5fsPt1zih.3OjMSceLE0CzCAGCdqXvXSnruEGgRPEXxRAeinKuZv3W_6eMpnVzkKFA.2xFMZXY9_ m9AwNUoi4bjSKVXxygjguB8m03gpwBhcPmm_FGLg21_vSCJWhdrEdU0PqXcByKio9OldCf5mr5EP DAGkSTzBHUTJR2Ws9acDW9hVc7R6T03lxG_W6vwG_IOJQCIPp0n49SbAv2DemRP9FFl_VimVErgG Kho..zvVoj0YxElkngxZHT4FAK8_1fzwESEkqsqOfvDXP7HIzF3Vt990DQFtMtNePmmQKwS9i1AY s0P47pTJhFuTEeYjz1.5Du.joZ12jDeF.MYUij6I5pc.MlY9ciO2wVvHZhU2zhNM0hPcExBlaHMr cJKvhj6vanupLpYTBc6f7_1nQh.v.FhGuc_RkCrnXgAe8jgkj0ZmXHJd339DCZKWBFEKZF9sy9Hp 78x3DxNaZL8sTELyw1njFTNAm6yzerVCI9zFAAOnFeRhdVFPGIkW4ys8f4s2pcFsDrZaLCduRJEM 7Kjv4iX2jSnv1s_q1WAJBWGy8E3pBgyTn_rN4M2Iha1KyOZYctukHvUfMZ_MgzXcT2vulxRbmi4u FvyzbHB3ebWuPxIAXGUkdxz4IadHf9do9lMZs..U_p9a430Ue3vg2nZb3rgwbKVLiHihytwDfGHQ JID090u8qiM9lMTH1zg_SK1AMFZC83ieAZyJNPLKt6kh5AZUrmCSjg47bPWOUTSbY74sQqKmHfc. QhF5fHubRe866XH4crTRsLNVVkUOp8KyGCVFYrtAMc4y0iF1O8xYOHVhhhQPohv0aLkl5TmgftfZ 9Q8iBU0ddPV5ZMkHsP.2th1wQo1H9qrAFv7ntBQEg4cl1IO_LcU6MBDO..dkIII7MT_90_LMD8TD Bc_X8jGjO59wMI_zpGGRcf9MtPqvIvT6B93EyK70- X-Sonic-MF: Received: from sonic.gate.mail.ne1.yahoo.com by sonic304.consmr.mail.bf2.yahoo.com with HTTP; Mon, 22 Feb 2021 16:53:50 +0000 Received: by smtp404.mail.ir2.yahoo.com (VZM Hermes SMTP Server) with ESMTPA ID 0b8dab11d16a58aea054514fd8669276; Mon, 22 Feb 2021 16:53:44 +0000 (UTC) Date: Mon, 22 Feb 2021 17:53:36 +0100 (CET) From: sgerwk-at-aol.com@ffmpeg.org To: FFmpeg development discussions and patches , sgerwk@aol.com In-Reply-To: <20210221073002.fhk3v6ccorr6svfe@jackie> Message-ID: <54b6212d-7ebd-78fc-c2b5-8156ab1c9683@aol.com> References: <26fa125b-357-dffb-1787-f7b14f8c667b.ref@aol.com> <26fa125b-357-dffb-1787-f7b14f8c667b@aol.com> <20210207195253.d6zm76mxigq3inkk@jackie> <4ef7cc3a-1d14-49e6-291d-c58ed84da898@aol.com> <20210221073002.fhk3v6ccorr6svfe@jackie> MIME-Version: 1.0 X-Mailer: WebService/1.1.17712 mail.backend.jedi.jws.acl:role.jedi.acl.token.atz.jws.hermes.aol Apache-HttpAsyncClient/4.1.4 (Java/11.0.9.1) Subject: [FFmpeg-devel] [PATCH v3] x11grab: capture a window instead of the whole screen 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: JhRdL90I69Kj Hi, On Sun, 21 Feb 2021, Andriy Gelman wrote: > Hi, > > Thanks for updating the patch. Sorry for the delay in getting you some feedback.. > > When I tested with -show_mouse 1 -show_region 1 -window_id xx, the mouse and > border get drawn in the wrong place. There is around (0.6cm error). Can you > reproduce? > I didn't notice the problem because the wm I use places the top-level windows in a window of the same size - at position 0,0. Still, I could reproduce it by capturing a subwindow. The problem was indeed what you suspected: geo->x,geo->y is the position inside the parent, but translate includes it already as it is the position within the root window. >> From e13c1be7abd6989b3ad80fd8086fe6a0819fd810 Mon Sep 17 00:00:00 2001 >> From: sgerwk >> Date: Wed, 10 Feb 2021 17:36:00 +0100 >> Subject: [PATCH] libavdevice/xcbgrab: option for grabbing a window instead of >> desktop >> >> --- >> doc/indevs.texi | 14 +++++++- >> libavdevice/xcbgrab.c | 79 ++++++++++++++++++++++++++++++++----------- >> 2 files changed, 72 insertions(+), 21 deletions(-) >> >> diff --git a/doc/indevs.texi b/doc/indevs.texi >> index 3924d03..48fd2b1 100644 >> --- a/doc/indevs.texi >> +++ b/doc/indevs.texi >> @@ -1564,8 +1564,20 @@ With @var{follow_mouse}: >> ffmpeg -f x11grab -follow_mouse centered -show_region 1 -framerate 25 -video_size cif -i :0.0 out.mpg >> @end example >> >> +@item window_id >> +Grab this window, instead of the whole screen. >> + >> +The id of a window can be found by xwininfo(1), possibly with options -tree and >> +-root. >> + >> +If the window is later enlarged, the new area is not recorded. Video ends when >> +the window is closed, unmapped (i.e., iconified) or shrunk beyond the video >> +size (which defaults to the initial window size). >> + >> +This option disables options @option{follow_mouse} and @option{select_region}. >> + >> @item video_size >> -Set the video frame size. Default is the full desktop. >> +Set the video frame size. Default is the full desktop or window. >> >> @item grab_x >> @item grab_y >> diff --git a/libavdevice/xcbgrab.c b/libavdevice/xcbgrab.c >> index be5d5ea..7697090 100644 >> --- a/libavdevice/xcbgrab.c >> +++ b/libavdevice/xcbgrab.c >> @@ -60,6 +60,8 @@ typedef struct XCBGrabContext { >> AVRational time_base; >> int64_t frame_duration; >> >> + xcb_window_t window_id; > >> + int win_x, win_y; > > The position of the window is always recalculated so I don't think win_x and > win_y should be part of the of XCBGrabContext. > Done. Some functions now require win_x and win_y because they no longer find them in XCBGrabContext. >> int x, y; >> int width, height; >> int frame_size; >> @@ -82,6 +84,7 @@ typedef struct XCBGrabContext { >> #define OFFSET(x) offsetof(XCBGrabContext, x) >> #define D AV_OPT_FLAG_DECODING_PARAM >> static const AVOption options[] = { >> + { "window_id", "Window to capture", OFFSET(window_id), AV_OPT_TYPE_INT, { .i64 = XCB_NONE }, 0, UINT32_MAX, D }, >> { "x", "Initial x coordinate.", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, >> { "y", "Initial y coordinate.", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, >> { "grab_x", "Initial x coordinate.", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, >> @@ -157,7 +160,7 @@ static int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt) >> XCBGrabContext *c = s->priv_data; >> xcb_get_image_cookie_t iq; >> xcb_get_image_reply_t *img; >> - xcb_drawable_t drawable = c->screen->root; >> + xcb_drawable_t drawable = c->window_id; >> xcb_generic_error_t *e = NULL; >> uint8_t *data; >> int length; >> @@ -267,7 +270,7 @@ static int xcbgrab_frame_shm(AVFormatContext *s, AVPacket *pkt) >> XCBGrabContext *c = s->priv_data; >> xcb_shm_get_image_cookie_t iq; >> xcb_shm_get_image_reply_t *img; >> - xcb_drawable_t drawable = c->screen->root; >> + xcb_drawable_t drawable = c->window_id; >> xcb_generic_error_t *e = NULL; >> AVBufferRef *buf; >> xcb_shm_seg_t segment; >> @@ -355,17 +358,17 @@ static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt, >> cx = ci->x - ci->xhot; >> cy = ci->y - ci->yhot; >> >> - x = FFMAX(cx, gr->x); >> - y = FFMAX(cy, gr->y); >> + x = FFMAX(cx, gr->win_x + gr->x); >> + y = FFMAX(cy, gr->win_y + gr->y); >> >> - w = FFMIN(cx + ci->width, gr->x + gr->width) - x; >> - h = FFMIN(cy + ci->height, gr->y + gr->height) - y; >> + w = FFMIN(cx + ci->width, gr->win_x + gr->x + gr->width) - x; >> + h = FFMIN(cy + ci->height, gr->win_y + gr->y + gr->height) - y; >> >> c_off = x - cx; >> - i_off = x - gr->x; >> + i_off = x - gr->x - gr->win_x; >> >> cursor += (y - cy) * ci->width; >> - image += (y - gr->y) * gr->width * stride; >> + image += (y - gr->y - gr->win_y) * gr->width * stride; >> >> for (y = 0; y < h; y++) { >> cursor += c_off; >> @@ -403,8 +406,8 @@ static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt, >> static void xcbgrab_update_region(AVFormatContext *s) >> { >> XCBGrabContext *c = s->priv_data; >> - const uint32_t args[] = { c->x - c->region_border, >> - c->y - c->region_border }; >> + const uint32_t args[] = { c->win_x + c->x - c->region_border, >> + c->win_y + c->y - c->region_border }; >> >> xcb_configure_window(c->conn, >> c->window, >> @@ -417,16 +420,22 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) >> XCBGrabContext *c = s->priv_data; >> xcb_query_pointer_cookie_t pc; >> xcb_get_geometry_cookie_t gc; >> + xcb_translate_coordinates_cookie_t tc; >> xcb_query_pointer_reply_t *p = NULL; >> xcb_get_geometry_reply_t *geo = NULL; >> + xcb_translate_coordinates_reply_t *translate = NULL; >> int ret = 0; >> int64_t pts; >> >> pts = wait_frame(s, pkt); >> >> - 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->window_id == c->screen->root) { >> + c->win_x = 0; >> + c->win_y = 0; >> + } >> + if (c->follow_mouse || c->draw_mouse || c->window_id != c->screen->root) { >> + pc = xcb_query_pointer(c->conn, c->window_id); >> + gc = xcb_get_geometry(c->conn, c->window_id); >> p = xcb_query_pointer_reply(c->conn, pc, NULL); >> if (!p) { >> av_log(s, AV_LOG_ERROR, "Failed to query xcb pointer\n"); >> @@ -438,6 +447,12 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) >> free(p); >> return AVERROR_EXTERNAL; >> } > >> + tc = xcb_translate_coordinates(c->conn, c->window_id, c->screen->root, geo->x, geo->y); > > It would cleaner if you move win_x,win_y calculation to a separate if statement. > (I don't think you are going to end up reusing geo->x, geo->y anyway because probably > that's what causing the mouse/border offset error). > Actually, this if is executed in the default case because of draw_mouse. But still, the code looks better with a separate if, so I did it. >> + translate = xcb_translate_coordinates_reply(c->conn, tc, NULL); > >> + if (!translate) >> + return AVERROR_EXTERNAL; > > p and geo need to be freed on this error. > You also need free translate when cleaning up at the end of this function. Done. I free translate immediately after getting win_x and win_y. > >> + c->win_x = translate->dst_x; >> + c->win_y = translate->dst_y; >> } >> >> if (c->follow_mouse && p->same_screen) >> @@ -447,7 +462,7 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) >> xcbgrab_update_region(s); >> >> #if CONFIG_LIBXCB_SHM > >> - if (c->has_shm && xcbgrab_frame_shm(s, pkt) < 0) { >> + if (c->has_shm && (ret = xcbgrab_frame_shm(s, pkt)) == AVERROR(ENOMEM)) { >> av_log(s, AV_LOG_WARNING, "Continuing without shared memory.\n"); >> c->has_shm = 0; >> } > > IMO this should be a separate commit as it changes how EACCESS is interpreted > even when the new option is not used. > I now just don't remember why I changed this, so I restored the previous condition. >> @@ -558,7 +573,9 @@ static int create_stream(AVFormatContext *s) >> XCBGrabContext *c = s->priv_data; >> AVStream *st = avformat_new_stream(s, NULL); >> xcb_get_geometry_cookie_t gc; >> + xcb_translate_coordinates_cookie_t tc; >> xcb_get_geometry_reply_t *geo; >> + xcb_translate_coordinates_reply_t *translate; >> int64_t frame_size_bits; >> int ret; >> >> @@ -571,11 +588,20 @@ static int create_stream(AVFormatContext *s) >> >> avpriv_set_pts_info(st, 64, 1, 1000000); >> >> - gc = xcb_get_geometry(c->conn, c->screen->root); >> + gc = xcb_get_geometry(c->conn, c->window_id); >> geo = xcb_get_geometry_reply(c->conn, gc, NULL); >> - if (!geo) >> + if (!geo) { >> + av_log(s, AV_LOG_ERROR, "Can't find window '0x%x', aborting.\n", c->window_id); >> + return AVERROR_EXTERNAL; >> + } >> + >> + tc = xcb_translate_coordinates(c->conn, c->window_id, c->screen->root, geo->x, geo->y); >> + translate = xcb_translate_coordinates_reply(c->conn, tc, NULL); > >> + if (!translate) >> return AVERROR_EXTERNAL; > > error would leak geo. > and translate will need to be freed somewhere if there's no error. > >> >> + c->win_x = translate->dst_x; >> + c->win_y = translate->dst_y; >> if (!c->width || !c->height) { >> c->width = geo->width; >> c->height = geo->height; > > You calculate win_x/win_y, but it actually doesn't end up being used anywhere in > xcbgrab_read_header(), and will get recalculated in xcbgrab_read_packet(). > You probably meant to also update setup_window()? > This calculation doesn't seem necessary, indeed. I removed it. > (btw, currently the border doesn't end being displayed until the first > xcbgrab_read_packet() call because there is no xcb_flush(c->conn) after > setup_window(s). But it could be added to display the initial border.) > I guess this should go in a separate commit, as it also affect the case when window_id is not set. > >> @@ -750,7 +776,7 @@ static int select_region(AVFormatContext *s) >> press_position = (xcb_point_t){ press->event_x, press->event_y }; >> rectangle.x = press_position.x; >> rectangle.y = press_position.y; >> - xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); >> + xcb_poly_rectangle(conn, c->window_id, gc, 1, &rectangle); >> was_pressed = 1; >> break; >> } >> @@ -759,14 +785,14 @@ static int select_region(AVFormatContext *s) >> xcb_motion_notify_event_t *motion = >> (xcb_motion_notify_event_t *)event; >> xcb_point_t cursor_position = { motion->event_x, motion->event_y }; >> - xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); >> + xcb_poly_rectangle(conn, c->window_id, gc, 1, &rectangle); >> rectangle = rectangle_from_corners(&press_position, &cursor_position); >> - xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); >> + xcb_poly_rectangle(conn, c->window_id, gc, 1, &rectangle); >> } >> break; >> } >> case XCB_BUTTON_RELEASE: { >> - xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); >> + xcb_poly_rectangle(conn, c->window_id, gc, 1, &rectangle); >> done = 1; >> break; >> } >> @@ -830,6 +856,19 @@ static av_cold int xcbgrab_read_header(AVFormatContext *s) >> return AVERROR(EIO); >> } > > You disabled select_region() earlier. These changes can be skipped then. > I disagree. The captured window is c->window_id now. Even if this part of code is currently unreachable if window_id is not the root window, a future commit may make it reachable again. >> >> + if (c->window_id == XCB_NONE) >> + c->window_id = c->screen->root; >> + else { >> + if (c->select_region) { >> + av_log(s, AV_LOG_WARNING, "select_region ignored with window_id.\n"); >> + c->select_region = 0; >> + } >> + if (c->follow_mouse) { >> + av_log(s, AV_LOG_WARNING, "follow_mouse ignored with window_id.\n"); >> + c->follow_mouse = 0; >> + } >> + } >> + >> if (c->select_region) { >> ret = select_region(s); > > Thanks, > -- > Andriy > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". Thanks! From 36e6c5a858415106d4d2d9a76fa45cbd59717c77 Mon Sep 17 00:00:00 2001 From: sgerwk Date: Wed, 10 Feb 2021 17:36:00 +0100 Subject: [PATCH] libavdevice/xcbgrab: option for grabbing a window instead of desktop --- doc/indevs.texi | 14 ++++++- libavdevice/xcbgrab.c | 93 +++++++++++++++++++++++++++++++------------ 2 files changed, 80 insertions(+), 27 deletions(-) diff --git a/doc/indevs.texi b/doc/indevs.texi index 3924d03..48fd2b1 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -1564,8 +1564,20 @@ With @var{follow_mouse}: ffmpeg -f x11grab -follow_mouse centered -show_region 1 -framerate 25 -video_size cif -i :0.0 out.mpg @end example +@item window_id +Grab this window, instead of the whole screen. + +The id of a window can be found by xwininfo(1), possibly with options -tree and +-root. + +If the window is later enlarged, the new area is not recorded. Video ends when +the window is closed, unmapped (i.e., iconified) or shrunk beyond the video +size (which defaults to the initial window size). + +This option disables options @option{follow_mouse} and @option{select_region}. + @item video_size -Set the video frame size. Default is the full desktop. +Set the video frame size. Default is the full desktop or window. @item grab_x @item grab_y diff --git a/libavdevice/xcbgrab.c b/libavdevice/xcbgrab.c index be5d5ea..ae935ad 100644 --- a/libavdevice/xcbgrab.c +++ b/libavdevice/xcbgrab.c @@ -60,6 +60,7 @@ typedef struct XCBGrabContext { AVRational time_base; int64_t frame_duration; + xcb_window_t window_id; int x, y; int width, height; int frame_size; @@ -82,6 +83,7 @@ typedef struct XCBGrabContext { #define OFFSET(x) offsetof(XCBGrabContext, x) #define D AV_OPT_FLAG_DECODING_PARAM static const AVOption options[] = { + { "window_id", "Window to capture", OFFSET(window_id), AV_OPT_TYPE_INT, { .i64 = XCB_NONE }, 0, UINT32_MAX, D }, { "x", "Initial x coordinate.", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, { "y", "Initial y coordinate.", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, { "grab_x", "Initial x coordinate.", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, @@ -108,7 +110,8 @@ static const AVClass xcbgrab_class = { static int xcbgrab_reposition(AVFormatContext *s, xcb_query_pointer_reply_t *p, - xcb_get_geometry_reply_t *geo) + xcb_get_geometry_reply_t *geo, + int win_x, int win_y) { XCBGrabContext *c = s->priv_data; int x = c->x, y = c->y; @@ -118,8 +121,8 @@ static int xcbgrab_reposition(AVFormatContext *s, if (!p || !geo) return AVERROR(EIO); - p_x = p->win_x; - p_y = p->win_y; + p_x = win_x; + p_y = win_y; if (f == FOLLOW_CENTER) { x = p_x - w / 2; @@ -157,7 +160,7 @@ static int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt) XCBGrabContext *c = s->priv_data; xcb_get_image_cookie_t iq; xcb_get_image_reply_t *img; - xcb_drawable_t drawable = c->screen->root; + xcb_drawable_t drawable = c->window_id; xcb_generic_error_t *e = NULL; uint8_t *data; int length; @@ -267,7 +270,7 @@ static int xcbgrab_frame_shm(AVFormatContext *s, AVPacket *pkt) XCBGrabContext *c = s->priv_data; xcb_shm_get_image_cookie_t iq; xcb_shm_get_image_reply_t *img; - xcb_drawable_t drawable = c->screen->root; + xcb_drawable_t drawable = c->window_id; xcb_generic_error_t *e = NULL; AVBufferRef *buf; xcb_shm_seg_t segment; @@ -333,7 +336,8 @@ static int check_xfixes(xcb_connection_t *conn) static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt, xcb_query_pointer_reply_t *p, - xcb_get_geometry_reply_t *geo) + xcb_get_geometry_reply_t *geo, + int win_x, int win_y) { XCBGrabContext *gr = s->priv_data; uint32_t *cursor; @@ -355,17 +359,17 @@ static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt, cx = ci->x - ci->xhot; cy = ci->y - ci->yhot; - x = FFMAX(cx, gr->x); - y = FFMAX(cy, gr->y); + x = FFMAX(cx, win_x + gr->x); + y = FFMAX(cy, win_y + gr->y); - w = FFMIN(cx + ci->width, gr->x + gr->width) - x; - h = FFMIN(cy + ci->height, gr->y + gr->height) - y; + w = FFMIN(cx + ci->width, win_x + gr->x + gr->width) - x; + h = FFMIN(cy + ci->height, win_y + gr->y + gr->height) - y; c_off = x - cx; - i_off = x - gr->x; + i_off = x - gr->x - win_x; cursor += (y - cy) * ci->width; - image += (y - gr->y) * gr->width * stride; + image += (y - gr->y - win_y) * gr->width * stride; for (y = 0; y < h; y++) { cursor += c_off; @@ -400,11 +404,11 @@ static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt, } #endif /* CONFIG_LIBXCB_XFIXES */ -static void xcbgrab_update_region(AVFormatContext *s) +static void xcbgrab_update_region(AVFormatContext *s, int win_x, int win_y) { XCBGrabContext *c = s->priv_data; - const uint32_t args[] = { c->x - c->region_border, - c->y - c->region_border }; + const uint32_t args[] = { win_x + c->x - c->region_border, + win_y + c->y - c->region_border }; xcb_configure_window(c->conn, c->window, @@ -417,16 +421,23 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) XCBGrabContext *c = s->priv_data; xcb_query_pointer_cookie_t pc; xcb_get_geometry_cookie_t gc; + xcb_translate_coordinates_cookie_t tc; xcb_query_pointer_reply_t *p = NULL; xcb_get_geometry_reply_t *geo = NULL; + xcb_translate_coordinates_reply_t *translate = NULL; int ret = 0; int64_t pts; + int win_x, win_y; pts = wait_frame(s, pkt); + if (c->window_id == c->screen->root) { + win_x = 0; + win_y = 0; + } 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); + pc = xcb_query_pointer(c->conn, c->window_id); + gc = xcb_get_geometry(c->conn, c->window_id); p = xcb_query_pointer_reply(c->conn, pc, NULL); if (!p) { av_log(s, AV_LOG_ERROR, "Failed to query xcb pointer\n"); @@ -439,12 +450,27 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EXTERNAL; } } + if (c->window_id != c->screen->root) { + tc = xcb_translate_coordinates(c->conn, c->window_id, c->screen->root, 0, 0); + translate = xcb_translate_coordinates_reply(c->conn, tc, NULL); + if (!translate) { + if (p != NULL) + free(p); + if (geo != NULL) + free(geo); + av_log(s, AV_LOG_ERROR, "Failed to translate xcb geometry\n"); + return AVERROR_EXTERNAL; + } + win_x = translate->dst_x; + win_y = translate->dst_y; + free(translate); + } if (c->follow_mouse && p->same_screen) - xcbgrab_reposition(s, p, geo); + xcbgrab_reposition(s, p, geo, win_x, win_y); if (c->show_region) - xcbgrab_update_region(s); + xcbgrab_update_region(s, win_x, win_y); #if CONFIG_LIBXCB_SHM if (c->has_shm && xcbgrab_frame_shm(s, pkt) < 0) { @@ -459,7 +485,7 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) #if CONFIG_LIBXCB_XFIXES if (ret >= 0 && c->draw_mouse && p->same_screen) - xcbgrab_draw_mouse(s, pkt, p, geo); + xcbgrab_draw_mouse(s, pkt, p, geo, win_x, win_y); #endif free(p); @@ -571,10 +597,12 @@ static int create_stream(AVFormatContext *s) avpriv_set_pts_info(st, 64, 1, 1000000); - gc = xcb_get_geometry(c->conn, c->screen->root); + gc = xcb_get_geometry(c->conn, c->window_id); geo = xcb_get_geometry_reply(c->conn, gc, NULL); - if (!geo) + if (!geo) { + av_log(s, AV_LOG_ERROR, "Can't find window '0x%x', aborting.\n", c->window_id); return AVERROR_EXTERNAL; + } if (!c->width || !c->height) { c->width = geo->width; @@ -750,7 +778,7 @@ static int select_region(AVFormatContext *s) press_position = (xcb_point_t){ press->event_x, press->event_y }; rectangle.x = press_position.x; rectangle.y = press_position.y; - xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); + xcb_poly_rectangle(conn, c->window_id, gc, 1, &rectangle); was_pressed = 1; break; } @@ -759,14 +787,14 @@ static int select_region(AVFormatContext *s) xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event; xcb_point_t cursor_position = { motion->event_x, motion->event_y }; - xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); + xcb_poly_rectangle(conn, c->window_id, gc, 1, &rectangle); rectangle = rectangle_from_corners(&press_position, &cursor_position); - xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); + xcb_poly_rectangle(conn, c->window_id, gc, 1, &rectangle); } break; } case XCB_BUTTON_RELEASE: { - xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); + xcb_poly_rectangle(conn, c->window_id, gc, 1, &rectangle); done = 1; break; } @@ -830,6 +858,19 @@ static av_cold int xcbgrab_read_header(AVFormatContext *s) return AVERROR(EIO); } + if (c->window_id == XCB_NONE) + c->window_id = c->screen->root; + else { + if (c->select_region) { + av_log(s, AV_LOG_WARNING, "select_region ignored with window_id.\n"); + c->select_region = 0; + } + if (c->follow_mouse) { + av_log(s, AV_LOG_WARNING, "follow_mouse ignored with window_id.\n"); + c->follow_mouse = 0; + } + } + if (c->select_region) { ret = select_region(s); if (ret < 0) {