mirror of https://git.ffmpeg.org/ffmpeg.git
avdevice/xcbgrab: Add select_region option
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. Reviewed-by: Andriy Gelman <andriy.gelman@gmail.com> Signed-off-by: Omar Emara <mail@OmarEmara.dev>
This commit is contained in:
parent
c8101aabee
commit
04b37b5d7f
|
@ -1503,6 +1503,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}.
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
|
||||
|
@ -677,6 +680,117 @@ 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, done = 0, 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;
|
||||
|
@ -711,6 +825,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) {
|
||||
|
|
Loading…
Reference in New Issue