[FFmpeg-devel] [PATCH v6] avdevice/xcbgrab: Add select_region option
Andriy Gelman
andriy.gelman at gmail.com
Tue Nov 3 07:25:44 EET 2020
On Thu, 08. Oct 23:40, Andriy Gelman wrote:
> On Sat, 11. Jul 11:29, Omar Emara wrote:
> > This patch adds a select_region option to the xcbgrab input device.
> > If set to 1, the user will be prompted to select the grabbing area
> > graphically by clicking and dragging. A rectangle will be drawn to
> > mark the grabbing area. A single click with no dragging will select
> > the whole screen. The option overwrites the video_size, grab_x, and
> > grab_y options if set by the user.
> >
> > For testing, just set the select_region option as follows:
> >
> > ffmpeg -f x11grab -select_region 1 -i :0.0 output.mp4
> >
> > The drawing happens directly on the root window using standard rubber
> > banding techniques, so it is very efficient and doesn't depend on any
> > X extensions or compositors.
> >
> > Signed-off-by: Omar Emara <mail at OmarEmara.dev>
> > ---
> > doc/indevs.texi | 8 +++
> > libavdevice/xcbgrab.c | 127 ++++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 135 insertions(+)
> >
> > diff --git a/doc/indevs.texi b/doc/indevs.texi
> > index 6f5afaf344..90ccc917aa 100644
> > --- a/doc/indevs.texi
> > +++ b/doc/indevs.texi
> > @@ -1478,6 +1478,14 @@ ffmpeg -f x11grab -framerate 25 -video_size cif -i :0.0+10,20 out.mpg
> > @subsection Options
> >
> > @table @option
> > + at item select_region
> > +Specify whether to select the grabbing area graphically using the pointer.
> > +A value of @code{1} prompts the user to select the grabbing area graphically
> > +by clicking and dragging. A single click with no dragging will select the
> > +whole screen. A region with zero width or height will also select the whole
> > +screen. This option overwrites the @var{video_size}, @var{grab_x}, and
> > + at var{grab_y} options. Default value is @code{0}.
> > +
> > @item draw_mouse
> > Specify whether to draw the mouse pointer. A value of @code{0} specifies
> > not to draw the pointer. Default value is @code{1}.
> > diff --git a/libavdevice/xcbgrab.c b/libavdevice/xcbgrab.c
> > index 6f6b2dbf15..bcd91e54af 100644
> > --- a/libavdevice/xcbgrab.c
> > +++ b/libavdevice/xcbgrab.c
> > @@ -22,6 +22,7 @@
> > #include "config.h"
> >
> > #include <stdlib.h>
> > +#include <string.h>
> > #include <xcb/xcb.h>
> >
> > #if CONFIG_LIBXCB_XFIXES
> > @@ -69,6 +70,7 @@ typedef struct XCBGrabContext {
> > int show_region;
> > int region_border;
> > int centered;
> > + int select_region;
> >
> > const char *framerate;
> >
> > @@ -92,6 +94,7 @@ static const AVOption options[] = {
> > { "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, "follow_mouse" },
> > { "show_region", "Show the grabbing region.", OFFSET(show_region), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D },
> > { "region_border", "Set the region border thickness.", OFFSET(region_border), AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 128, D },
> > + { "select_region", "Select the grabbing region graphically using the pointer.", OFFSET(select_region), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
> > { NULL },
> > };
> >
> > @@ -668,6 +671,122 @@ static void setup_window(AVFormatContext *s)
> > draw_rectangle(s);
> > }
> >
> > +#define CROSSHAIR_CURSOR 34
> > +
> > +static xcb_rectangle_t rectangle_from_corners(xcb_point_t *corner_a,
> > + xcb_point_t *corner_b)
> > +{
> > + xcb_rectangle_t rectangle;
> > + rectangle.x = FFMIN(corner_a->x, corner_b->x);
> > + rectangle.y = FFMIN(corner_a->y, corner_b->y);
> > + rectangle.width = FFABS(corner_a->x - corner_b->x);
> > + rectangle.height = FFABS(corner_a->y - corner_b->y);
> > + return rectangle;
> > +}
> > +
> > +static int select_region(AVFormatContext *s)
> > +{
> > + XCBGrabContext *c = s->priv_data;
> > + xcb_connection_t *conn = c->conn;
> > + xcb_screen_t *screen = c->screen;
> > +
> > + int ret = 0;
> > + int done = 0;
> > + int was_pressed = 0;
> > + xcb_cursor_t cursor;
> > + xcb_font_t cursor_font;
> > + xcb_point_t press_position;
> > + xcb_generic_event_t *event;
> > + xcb_rectangle_t rectangle = {0};
> > + xcb_grab_pointer_reply_t *reply;
> > + xcb_grab_pointer_cookie_t cookie;
> > +
> > + xcb_window_t root_window = screen->root;
> > + xcb_gcontext_t gc = xcb_generate_id(conn);
> > + uint32_t mask = XCB_GC_FUNCTION | XCB_GC_SUBWINDOW_MODE;
> > + uint32_t values[] = {XCB_GX_INVERT,
> > + XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS};
> > + xcb_create_gc(conn, gc, root_window, mask, values);
> > +
> > + cursor_font = xcb_generate_id(conn);
> > + xcb_open_font(conn, cursor_font, strlen("cursor"), "cursor");
> > + cursor = xcb_generate_id(conn);
> > + xcb_create_glyph_cursor(conn, cursor, cursor_font, cursor_font,
> > + CROSSHAIR_CURSOR, CROSSHAIR_CURSOR + 1, 0, 0, 0,
> > + 0xFFFF, 0xFFFF, 0xFFFF);
> > + cookie = xcb_grab_pointer(conn, 0, root_window,
> > + XCB_EVENT_MASK_BUTTON_PRESS |
> > + XCB_EVENT_MASK_BUTTON_RELEASE |
> > + XCB_EVENT_MASK_BUTTON_MOTION,
> > + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
> > + root_window, cursor, XCB_CURRENT_TIME);
> > + reply = xcb_grab_pointer_reply(conn, cookie, NULL);
> > + if (!reply || reply->status != XCB_GRAB_STATUS_SUCCESS) {
> > + av_log(s, AV_LOG_ERROR,
> > + "Failed to select region. Could not grab pointer.\n");
> > + ret = AVERROR(EIO);
> > + free(reply);
> > + goto fail;
> > + }
> > + free(reply);
> > +
> > + xcb_grab_server(conn);
> > +
> > + while (!done && (event = xcb_wait_for_event(conn))) {
> > + switch (event->response_type & ~0x80) {
> > + case XCB_BUTTON_PRESS: {
> > + xcb_button_press_event_t *press = (xcb_button_press_event_t *)event;
> > + 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);
> > + was_pressed = 1;
> > + break;
> > + }
> > + case XCB_MOTION_NOTIFY: {
> > + if (was_pressed) {
> > + 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);
> > + rectangle = rectangle_from_corners(
> > + &press_position, &cursor_position);
> > + xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle);
> > + }
> > + break;
> > + }
> > + case XCB_BUTTON_RELEASE: {
> > + xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle);
> > + done = 1;
> > + break;
> > + }
> > + default:
> > + break;
> > + }
> > + xcb_flush(conn);
> > + free(event);
> > + }
> > + c->width = rectangle.width;
> > + c->height = rectangle.height;
> > + if (c->width && c->height) {
> > + c->x = rectangle.x;
> > + c->y = rectangle.y;
> > + } else {
> > + c->x = 0;
> > + c->y = 0;
> > + }
> > + xcb_ungrab_server(conn);
> > + xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
> > + xcb_flush(conn);
> > +
> > +fail:
> > + xcb_free_cursor(conn, cursor);
> > + xcb_close_font(conn, cursor_font);
> > + xcb_free_gc(conn, gc);
> > + return ret;
> > +}
> > +
> > static av_cold int xcbgrab_read_header(AVFormatContext *s)
> > {
> > XCBGrabContext *c = s->priv_data;
> > @@ -702,6 +821,14 @@ static av_cold int xcbgrab_read_header(AVFormatContext *s)
> > return AVERROR(EIO);
> > }
> >
> > + if (c->select_region) {
> > + ret = select_region(s);
> > + if (ret < 0) {
> > + xcbgrab_read_close(s);
> > + return ret;
> > + }
> > + }
> > +
> > ret = create_stream(s);
> >
> > if (ret < 0) {
>
> Any more comments on this patch?
>
Will apply this patch on Friday if no one objects.
--
Andriy
More information about the ffmpeg-devel
mailing list