diff options
Diffstat (limited to 'src/capture')
-rw-r--r-- | src/capture/kms.c | 2 | ||||
-rw-r--r-- | src/capture/nvfbc.c | 1 | ||||
-rw-r--r-- | src/capture/portal.c | 3 | ||||
-rw-r--r-- | src/capture/xcomposite.c | 3 | ||||
-rw-r--r-- | src/capture/ximage.c | 247 |
5 files changed, 248 insertions, 8 deletions
diff --git a/src/capture/kms.c b/src/capture/kms.c index 9693c58..578fded 100644 --- a/src/capture/kms.c +++ b/src/capture/kms.c @@ -14,9 +14,7 @@ #include <xf86drm.h> #include <libdrm/drm_fourcc.h> -#include <libavcodec/avcodec.h> #include <libavutil/mastering_display_metadata.h> -#include <libavformat/avformat.h> #define FIND_CRTC_BY_NAME_TIMEOUT_SECONDS 2.0 diff --git a/src/capture/nvfbc.c b/src/capture/nvfbc.c index a0c8d29..188b321 100644 --- a/src/capture/nvfbc.c +++ b/src/capture/nvfbc.c @@ -13,7 +13,6 @@ #include <assert.h> #include <X11/Xlib.h> -#include <libavcodec/avcodec.h> typedef struct { gsr_capture_nvfbc_params params; diff --git a/src/capture/portal.c b/src/capture/portal.c index 893c07a..e065f02 100644 --- a/src/capture/portal.c +++ b/src/capture/portal.c @@ -8,10 +8,9 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> +#include <limits.h> #include <assert.h> -#include <libavcodec/avcodec.h> - typedef struct { gsr_capture_portal_params params; diff --git a/src/capture/xcomposite.c b/src/capture/xcomposite.c index 2016a31..d8f4c27 100644 --- a/src/capture/xcomposite.c +++ b/src/capture/xcomposite.c @@ -12,9 +12,6 @@ #include <X11/Xlib.h> -#include <libavutil/frame.h> -#include <libavcodec/avcodec.h> - typedef struct { gsr_capture_xcomposite_params params; Display *display; diff --git a/src/capture/ximage.c b/src/capture/ximage.c new file mode 100644 index 0000000..259761d --- /dev/null +++ b/src/capture/ximage.c @@ -0,0 +1,247 @@ +#include "../../include/capture/ximage.h" +#include "../../include/utils.h" +#include "../../include/cursor.h" +#include "../../include/color_conversion.h" +#include "../../include/window/window.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include <X11/Xlib.h> + +/* TODO: update when monitors are reconfigured */ + +typedef struct { + gsr_capture_ximage_params params; + Display *display; + gsr_cursor cursor; + gsr_monitor monitor; + vec2i capture_pos; + vec2i capture_size; + unsigned int texture_id; + Window root_window; +} gsr_capture_ximage; + +static void gsr_capture_ximage_stop(gsr_capture_ximage *self) { + gsr_cursor_deinit(&self->cursor); + if(self->texture_id) { + self->params.egl->glDeleteTextures(1, &self->texture_id); + self->texture_id = 0; + } +} + +static int max_int(int a, int b) { + return a > b ? a : b; +} + +static int gsr_capture_ximage_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata) { + gsr_capture_ximage *self = cap->priv; + self->root_window = DefaultRootWindow(self->display); + + if(gsr_cursor_init(&self->cursor, self->params.egl, self->display) != 0) { + gsr_capture_ximage_stop(self); + return -1; + } + + if(!get_monitor_by_name(self->params.egl, GSR_CONNECTION_X11, self->params.display_to_capture, &self->monitor)) { + fprintf(stderr, "gsr error: gsr_capture_ximage_start: failed to find monitor by name \"%s\"\n", self->params.display_to_capture); + gsr_capture_ximage_stop(self); + return -1; + } + + self->capture_pos = self->monitor.pos; + self->capture_size = self->monitor.size; + + if(self->params.region_size.x > 0 && self->params.region_size.y > 0) + self->capture_size = self->params.region_size; + + if(self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0) { + self->params.output_resolution = scale_keep_aspect_ratio(self->capture_size, self->params.output_resolution); + capture_metadata->width = self->params.output_resolution.x; + capture_metadata->height = self->params.output_resolution.y; + } else if(self->params.region_size.x > 0 && self->params.region_size.y > 0) { + capture_metadata->width = self->params.region_size.x; + capture_metadata->height = self->params.region_size.y; + } else { + capture_metadata->width = self->capture_size.x; + capture_metadata->height = self->capture_size.y; + } + + self->texture_id = gl_create_texture(self->params.egl, self->capture_size.x, self->capture_size.y, GL_RGB8, GL_RGB, GL_LINEAR); + if(self->texture_id == 0) { + fprintf(stderr, "gsr error: gsr_capture_ximage_start: failed to create texture\n"); + gsr_capture_ximage_stop(self); + return -1; + } + + return 0; +} + +static void gsr_capture_ximage_on_event(gsr_capture *cap, gsr_egl *egl) { + gsr_capture_ximage *self = cap->priv; + XEvent *xev = gsr_window_get_event_data(egl->window); + gsr_cursor_on_event(&self->cursor, xev); +} + +static bool gsr_capture_ximage_upload_to_texture(gsr_capture_ximage *self, int x, int y, int width, int height) { + const int max_width = XWidthOfScreen(DefaultScreenOfDisplay(self->display)); + const int max_height = XHeightOfScreen(DefaultScreenOfDisplay(self->display)); + + if(x < 0) + x = 0; + else if(x >= max_width) + x = max_width - 1; + + if(y < 0) + y = 0; + else if(y >= max_height) + y = max_height - 1; + + if(width < 0) + width = 0; + else if(x + width >= max_width) + width = max_width - x; + + if(height < 0) + height = 0; + else if(y + height >= max_height) + height = max_height - y; + + XImage *image = XGetImage(self->display, self->root_window, x, y, width, height, AllPlanes, ZPixmap); + if(!image) { + fprintf(stderr, "gsr error: gsr_capture_ximage_upload_to_texture: XGetImage failed\n"); + return false; + } + + bool success = false; + uint8_t *image_data = malloc(image->width * image->height * 3); + if(!image_data) { + fprintf(stderr, "gsr error: gsr_capture_ximage_upload_to_texture: failed to allocate image data\n"); + goto done; + } + + for(int y = 0; y < image->height; ++y) { + for(int x = 0; x < image->width; ++x) { + unsigned long pixel = XGetPixel(image, x, y); + unsigned char red = (pixel & image->red_mask) >> 16; + unsigned char green = (pixel & image->green_mask) >> 8; + unsigned char blue = pixel & image->blue_mask; + + const size_t texture_data_index = (x + y * image->width) * 3; + image_data[texture_data_index + 0] = red; + image_data[texture_data_index + 1] = green; + image_data[texture_data_index + 2] = blue; + } + } + + self->params.egl->glBindTexture(GL_TEXTURE_2D, self->texture_id); + self->params.egl->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image->width, image->height, GL_RGB, GL_UNSIGNED_BYTE, image_data); + self->params.egl->glBindTexture(GL_TEXTURE_2D, 0); + success = true; + + done: + free(image_data); + XDestroyImage(image); + return success; +} + +static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *capture_metdata, gsr_color_conversion *color_conversion) { + gsr_capture_ximage *self = cap->priv; + + const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0; + vec2i output_size = is_scaled ? self->params.output_resolution : self->capture_size; + output_size = scale_keep_aspect_ratio(self->capture_size, output_size); + + const vec2i target_pos = { max_int(0, capture_metdata->width / 2 - output_size.x / 2), max_int(0, capture_metdata->height / 2 - output_size.y / 2) }; + gsr_capture_ximage_upload_to_texture(self, self->capture_pos.x + self->params.region_position.x, self->capture_pos.y + self->params.region_position.y, self->capture_size.x, self->capture_size.y); + + gsr_color_conversion_draw(color_conversion, self->texture_id, + target_pos, output_size, + (vec2i){0, 0}, self->capture_size, + 0.0f, false, GSR_SOURCE_COLOR_RGB); + + if(self->params.record_cursor && self->cursor.visible) { + const vec2d scale = { + self->capture_size.x == 0 ? 0 : (double)output_size.x / (double)self->capture_size.x, + self->capture_size.y == 0 ? 0 : (double)output_size.y / (double)self->capture_size.y + }; + + gsr_cursor_tick(&self->cursor, self->root_window); + + const vec2i cursor_pos = { + target_pos.x + (self->cursor.position.x - self->cursor.hotspot.x) * scale.x - self->capture_pos.x - self->params.region_position.x, + target_pos.y + (self->cursor.position.y - self->cursor.hotspot.y) * scale.y - self->capture_pos.y - self->params.region_position.y + }; + + self->params.egl->glEnable(GL_SCISSOR_TEST); + self->params.egl->glScissor(target_pos.x, target_pos.y, output_size.x, output_size.y); + + gsr_color_conversion_draw(color_conversion, self->cursor.texture_id, + cursor_pos, (vec2i){self->cursor.size.x * scale.x, self->cursor.size.y * scale.y}, + (vec2i){0, 0}, self->cursor.size, + 0.0f, false, GSR_SOURCE_COLOR_RGB); + + self->params.egl->glDisable(GL_SCISSOR_TEST); + } + + self->params.egl->glFlush(); + self->params.egl->glFinish(); + + return 0; +} + +static void gsr_capture_ximage_destroy(gsr_capture *cap) { + gsr_capture_ximage *self = cap->priv; + if(cap->priv) { + gsr_capture_ximage_stop(self); + free((void*)self->params.display_to_capture); + self->params.display_to_capture = NULL; + free(self); + cap->priv = NULL; + } + free(cap); +} + +gsr_capture* gsr_capture_ximage_create(const gsr_capture_ximage_params *params) { + if(!params) { + fprintf(stderr, "gsr error: gsr_capture_ximage_create params is NULL\n"); + return NULL; + } + + gsr_capture *cap = calloc(1, sizeof(gsr_capture)); + if(!cap) + return NULL; + + gsr_capture_ximage *cap_ximage = calloc(1, sizeof(gsr_capture_ximage)); + if(!cap_ximage) { + free(cap); + return NULL; + } + + const char *display_to_capture = strdup(params->display_to_capture); + if(!display_to_capture) { + free(cap); + free(cap_ximage); + return NULL; + } + + cap_ximage->params = *params; + cap_ximage->display = gsr_window_get_display(params->egl->window); + cap_ximage->params.display_to_capture = display_to_capture; + + *cap = (gsr_capture) { + .start = gsr_capture_ximage_start, + .on_event = gsr_capture_ximage_on_event, + .tick = NULL, + .should_stop = NULL, + .capture = gsr_capture_ximage_capture, + .uses_external_image = NULL, + .get_window_id = NULL, + .destroy = gsr_capture_ximage_destroy, + .priv = cap_ximage + }; + + return cap; +} |