Message ID | 20200711092909.115525-1-mail@OmarEmara.dev |
---|---|
State | Accepted |
Headers | show |
Series | [FFmpeg-devel,v6] avdevice/xcbgrab: Add select_region option | expand |
Context | Check | Description |
---|---|---|
andriy/default | pending | |
andriy/make | success | Make finished |
andriy/make_fate | success | Make fate finished |
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@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 > +@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 > +@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? Thanks,
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@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 > > +@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 > > +@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.
On Tue, 03. Nov 00:25, Andriy Gelman wrote: > 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@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 > > > +@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 > > > +@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. > Applied. Thanks,
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 +@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 +@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) {
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@OmarEmara.dev> --- doc/indevs.texi | 8 +++ libavdevice/xcbgrab.c | 127 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+)