]> git.sesse.net Git - ffmpeg/blobdiff - libavdevice/xcbgrab.c
avdevice/pulse_audio_dec: do not read undersized frames
[ffmpeg] / libavdevice / xcbgrab.c
index 8ef2a30d02fb7f78431f64bb8bdf62272bd639b2..f9ea5a56b2fd960d4bbab487a8c75d69cb54719a 100644 (file)
@@ -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 },
 };
 
@@ -203,7 +206,7 @@ static int64_t wait_frame(AVFormatContext *s, AVPacket *pkt)
     c->time_frame += c->frame_duration;
 
     for (;;) {
-        curtime = av_gettime();
+        curtime = av_gettime_relative();
         delay   = c->time_frame - curtime;
         if (delay <= 0)
             break;
@@ -233,7 +236,7 @@ static void free_shm_buffer(void *opaque, uint8_t *data)
     shmdt(data);
 }
 
-static AVBufferRef *allocate_shm_buffer(void *opaque, int size)
+static AVBufferRef *allocate_shm_buffer(void *opaque, buffer_size_t size)
 {
     xcb_connection_t *conn = opaque;
     xcb_shm_seg_t segment;
@@ -419,7 +422,8 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt)
     int ret = 0;
     int64_t pts;
 
-    pts = wait_frame(s, pkt);
+    wait_frame(s, pkt);
+    pts = av_gettime();
 
     if (c->follow_mouse || c->draw_mouse) {
         pc  = xcb_query_pointer(c->conn, c->screen->root);
@@ -510,21 +514,26 @@ static int pixfmt_from_pixmap_format(AVFormatContext *s, int depth,
             switch (depth) {
             case 32:
                 if (fmt->bits_per_pixel == 32)
-                    *pix_fmt = AV_PIX_FMT_0RGB;
+                    *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
+                               AV_PIX_FMT_BGR0 : AV_PIX_FMT_0RGB;
                 break;
             case 24:
                 if (fmt->bits_per_pixel == 32)
-                    *pix_fmt = AV_PIX_FMT_0RGB32;
+                    *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
+                               AV_PIX_FMT_BGR0 : AV_PIX_FMT_0RGB;
                 else if (fmt->bits_per_pixel == 24)
-                    *pix_fmt = AV_PIX_FMT_RGB24;
+                    *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
+                               AV_PIX_FMT_BGR24 : AV_PIX_FMT_RGB24;
                 break;
             case 16:
                 if (fmt->bits_per_pixel == 16)
-                    *pix_fmt = AV_PIX_FMT_RGB565;
+                    *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
+                               AV_PIX_FMT_RGB565LE : AV_PIX_FMT_RGB565BE;
                 break;
             case 15:
                 if (fmt->bits_per_pixel == 16)
-                    *pix_fmt = AV_PIX_FMT_RGB555;
+                    *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
+                               AV_PIX_FMT_RGB555LE : AV_PIX_FMT_RGB555BE;
                 break;
             case 8:
                 if (fmt->bits_per_pixel == 8)
@@ -588,7 +597,7 @@ static int create_stream(AVFormatContext *s)
     c->time_base  = (AVRational){ st->avg_frame_rate.den,
                                   st->avg_frame_rate.num };
     c->frame_duration = av_rescale_q(1, c->time_base, AV_TIME_BASE_Q);
-    c->time_frame = av_gettime();
+    c->time_frame = av_gettime_relative();
 
     ret = pixfmt_from_pixmap_format(s, geo->depth, &st->codecpar->format, &c->bpp);
     free(geo);
@@ -677,6 +686,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 +831,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) {