mirror of https://git.ffmpeg.org/ffmpeg.git
avdevice/xcbgrab: Add option for grabbing a window
The option allows to select a specific window instead of the whole screen. Reviewed-by: Andriy Gelman <andriy.gelman@gmail.com> Signed-off-by: Andriy Gelman <andriy.gelman@gmail.com>
This commit is contained in:
parent
1af4885014
commit
07de796b5d
|
@ -1564,8 +1564,21 @@ With @var{follow_mouse}:
|
||||||
ffmpeg -f x11grab -follow_mouse centered -show_region 1 -framerate 25 -video_size cif -i :0.0 out.mpg
|
ffmpeg -f x11grab -follow_mouse centered -show_region 1 -framerate 25 -video_size cif -i :0.0 out.mpg
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
|
@item window_id
|
||||||
|
Grab this window, instead of the whole screen. Default value is 0, which maps to
|
||||||
|
the whole screen (root window).
|
||||||
|
|
||||||
|
The id of a window can be found using the @command{xwininfo} program, 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
|
@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_x
|
||||||
@item grab_y
|
@item grab_y
|
||||||
|
|
|
@ -60,6 +60,7 @@ typedef struct XCBGrabContext {
|
||||||
AVRational time_base;
|
AVRational time_base;
|
||||||
int64_t frame_duration;
|
int64_t frame_duration;
|
||||||
|
|
||||||
|
xcb_window_t window_id;
|
||||||
int x, y;
|
int x, y;
|
||||||
int width, height;
|
int width, height;
|
||||||
int frame_size;
|
int frame_size;
|
||||||
|
@ -82,6 +83,7 @@ typedef struct XCBGrabContext {
|
||||||
#define OFFSET(x) offsetof(XCBGrabContext, x)
|
#define OFFSET(x) offsetof(XCBGrabContext, x)
|
||||||
#define D AV_OPT_FLAG_DECODING_PARAM
|
#define D AV_OPT_FLAG_DECODING_PARAM
|
||||||
static const AVOption options[] = {
|
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 },
|
{ "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 },
|
{ "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 },
|
{ "grab_x", "Initial x coordinate.", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D },
|
||||||
|
@ -157,7 +159,7 @@ static int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt)
|
||||||
XCBGrabContext *c = s->priv_data;
|
XCBGrabContext *c = s->priv_data;
|
||||||
xcb_get_image_cookie_t iq;
|
xcb_get_image_cookie_t iq;
|
||||||
xcb_get_image_reply_t *img;
|
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;
|
xcb_generic_error_t *e = NULL;
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
int length;
|
int length;
|
||||||
|
@ -267,7 +269,7 @@ static int xcbgrab_frame_shm(AVFormatContext *s, AVPacket *pkt)
|
||||||
XCBGrabContext *c = s->priv_data;
|
XCBGrabContext *c = s->priv_data;
|
||||||
xcb_shm_get_image_cookie_t iq;
|
xcb_shm_get_image_cookie_t iq;
|
||||||
xcb_shm_get_image_reply_t *img;
|
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;
|
xcb_generic_error_t *e = NULL;
|
||||||
AVBufferRef *buf;
|
AVBufferRef *buf;
|
||||||
xcb_shm_seg_t segment;
|
xcb_shm_seg_t segment;
|
||||||
|
@ -333,7 +335,8 @@ static int check_xfixes(xcb_connection_t *conn)
|
||||||
|
|
||||||
static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt,
|
static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt,
|
||||||
xcb_query_pointer_reply_t *p,
|
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;
|
XCBGrabContext *gr = s->priv_data;
|
||||||
uint32_t *cursor;
|
uint32_t *cursor;
|
||||||
|
@ -355,17 +358,17 @@ static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt,
|
||||||
cx = ci->x - ci->xhot;
|
cx = ci->x - ci->xhot;
|
||||||
cy = ci->y - ci->yhot;
|
cy = ci->y - ci->yhot;
|
||||||
|
|
||||||
x = FFMAX(cx, gr->x);
|
x = FFMAX(cx, win_x + gr->x);
|
||||||
y = FFMAX(cy, gr->y);
|
y = FFMAX(cy, win_y + gr->y);
|
||||||
|
|
||||||
w = FFMIN(cx + ci->width, gr->x + gr->width) - x;
|
w = FFMIN(cx + ci->width, win_x + gr->x + gr->width) - x;
|
||||||
h = FFMIN(cy + ci->height, gr->y + gr->height) - y;
|
h = FFMIN(cy + ci->height, win_y + gr->y + gr->height) - y;
|
||||||
|
|
||||||
c_off = x - cx;
|
c_off = x - cx;
|
||||||
i_off = x - gr->x;
|
i_off = x - gr->x - win_x;
|
||||||
|
|
||||||
cursor += (y - cy) * ci->width;
|
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++) {
|
for (y = 0; y < h; y++) {
|
||||||
cursor += c_off;
|
cursor += c_off;
|
||||||
|
@ -400,11 +403,11 @@ static void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt,
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_LIBXCB_XFIXES */
|
#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;
|
XCBGrabContext *c = s->priv_data;
|
||||||
const uint32_t args[] = { c->x - c->region_border,
|
const uint32_t args[] = { win_x + c->x - c->region_border,
|
||||||
c->y - c->region_border };
|
win_y + c->y - c->region_border };
|
||||||
|
|
||||||
xcb_configure_window(c->conn,
|
xcb_configure_window(c->conn,
|
||||||
c->window,
|
c->window,
|
||||||
|
@ -417,17 +420,20 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||||
XCBGrabContext *c = s->priv_data;
|
XCBGrabContext *c = s->priv_data;
|
||||||
xcb_query_pointer_cookie_t pc;
|
xcb_query_pointer_cookie_t pc;
|
||||||
xcb_get_geometry_cookie_t gc;
|
xcb_get_geometry_cookie_t gc;
|
||||||
|
xcb_translate_coordinates_cookie_t tc;
|
||||||
xcb_query_pointer_reply_t *p = NULL;
|
xcb_query_pointer_reply_t *p = NULL;
|
||||||
xcb_get_geometry_reply_t *geo = NULL;
|
xcb_get_geometry_reply_t *geo = NULL;
|
||||||
|
xcb_translate_coordinates_reply_t *translate = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int64_t pts;
|
int64_t pts;
|
||||||
|
int win_x = 0, win_y = 0;
|
||||||
|
|
||||||
wait_frame(s, pkt);
|
wait_frame(s, pkt);
|
||||||
pts = av_gettime();
|
pts = av_gettime();
|
||||||
|
|
||||||
if (c->follow_mouse || c->draw_mouse) {
|
if (c->follow_mouse || c->draw_mouse) {
|
||||||
pc = xcb_query_pointer(c->conn, c->screen->root);
|
pc = xcb_query_pointer(c->conn, c->window_id);
|
||||||
gc = xcb_get_geometry(c->conn, c->screen->root);
|
gc = xcb_get_geometry(c->conn, c->window_id);
|
||||||
p = xcb_query_pointer_reply(c->conn, pc, NULL);
|
p = xcb_query_pointer_reply(c->conn, pc, NULL);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
av_log(s, AV_LOG_ERROR, "Failed to query xcb pointer\n");
|
av_log(s, AV_LOG_ERROR, "Failed to query xcb pointer\n");
|
||||||
|
@ -440,12 +446,25 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||||
return AVERROR_EXTERNAL;
|
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) {
|
||||||
|
free(p);
|
||||||
|
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)
|
if (c->follow_mouse && p->same_screen)
|
||||||
xcbgrab_reposition(s, p, geo);
|
xcbgrab_reposition(s, p, geo);
|
||||||
|
|
||||||
if (c->show_region)
|
if (c->show_region)
|
||||||
xcbgrab_update_region(s);
|
xcbgrab_update_region(s, win_x, win_y);
|
||||||
|
|
||||||
#if CONFIG_LIBXCB_SHM
|
#if CONFIG_LIBXCB_SHM
|
||||||
if (c->has_shm && xcbgrab_frame_shm(s, pkt) < 0) {
|
if (c->has_shm && xcbgrab_frame_shm(s, pkt) < 0) {
|
||||||
|
@ -460,7 +479,7 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||||
|
|
||||||
#if CONFIG_LIBXCB_XFIXES
|
#if CONFIG_LIBXCB_XFIXES
|
||||||
if (ret >= 0 && c->draw_mouse && p->same_screen)
|
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
|
#endif
|
||||||
|
|
||||||
free(p);
|
free(p);
|
||||||
|
@ -572,10 +591,12 @@ static int create_stream(AVFormatContext *s)
|
||||||
|
|
||||||
avpriv_set_pts_info(st, 64, 1, 1000000);
|
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);
|
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;
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (!c->width || !c->height) {
|
if (!c->width || !c->height) {
|
||||||
c->width = geo->width;
|
c->width = geo->width;
|
||||||
|
@ -831,6 +852,19 @@ static av_cold int xcbgrab_read_header(AVFormatContext *s)
|
||||||
return AVERROR(EIO);
|
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) {
|
if (c->select_region) {
|
||||||
ret = select_region(s);
|
ret = select_region(s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
Loading…
Reference in New Issue