aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2022-12-01 00:47:30 +0100
committerdec05eba <dec05eba@protonmail.com>2022-12-20 15:32:42 +0100
commit4e6fc174fe02d3ddb0d3dfe5894a31502df9b1ed (patch)
tree033dca81f26edf5f662406b902ca6fe6e2fd4f5e /src
parent6a6bb703bce2d844175950ede4bda5d06dc5a8ae (diff)
follow focused
Diffstat (limited to 'src')
-rw-r--r--src/capture/nvfbc.c14
-rw-r--r--src/capture/xcomposite_cuda.c191
-rw-r--r--src/capture/xcomposite_drm.c27
-rw-r--r--src/egl.c112
-rw-r--r--src/main.cpp168
-rw-r--r--src/window_texture.c67
6 files changed, 322 insertions, 257 deletions
diff --git a/src/capture/nvfbc.c b/src/capture/nvfbc.c
index 17aedf8..ff3f9b7 100644
--- a/src/capture/nvfbc.c
+++ b/src/capture/nvfbc.c
@@ -344,8 +344,11 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec
cap_nvfbc->fbc_handle_created = false;
}
- av_buffer_unref(&video_codec_context->hw_device_ctx);
- av_buffer_unref(&video_codec_context->hw_frames_ctx);
+ if(video_codec_context->hw_device_ctx)
+ av_buffer_unref(&video_codec_context->hw_device_ctx);
+ // Not needed because the above call to unref device ctx also frees this?
+ //if(video_codec_context->hw_frames_ctx)
+ // av_buffer_unref(&video_codec_context->hw_frames_ctx);
gsr_cuda_unload(&cap_nvfbc->cuda);
return -1;
}
@@ -413,8 +416,11 @@ static int gsr_capture_nvfbc_capture(gsr_capture *cap, AVFrame *frame) {
static void gsr_capture_nvfbc_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) {
gsr_capture_nvfbc *cap_nvfbc = cap->priv;
gsr_capture_nvfbc_destroy_session(cap);
- av_buffer_unref(&video_codec_context->hw_device_ctx);
- av_buffer_unref(&video_codec_context->hw_frames_ctx);
+ if(video_codec_context->hw_device_ctx)
+ av_buffer_unref(&video_codec_context->hw_device_ctx);
+ // Not needed because the above call to unref device ctx also frees this?
+ //if(video_codec_context->hw_frames_ctx)
+ // av_buffer_unref(&video_codec_context->hw_frames_ctx);
if(cap_nvfbc) {
gsr_cuda_unload(&cap_nvfbc->cuda);
dlclose(cap_nvfbc->library);
diff --git a/src/capture/xcomposite_cuda.c b/src/capture/xcomposite_cuda.c
index d6c147b..6b2b72c 100644
--- a/src/capture/xcomposite_cuda.c
+++ b/src/capture/xcomposite_cuda.c
@@ -20,12 +20,12 @@ typedef struct {
double window_resize_timer;
vec2i window_size;
- vec2i window_pos;
unsigned int target_texture_id;
vec2i texture_size;
- Window composite_window;
+ Window window;
WindowTexture window_texture;
+ Atom net_active_window_atom;
CUgraphicsResource cuda_graphics_resource;
CUarray mapped_array;
@@ -42,6 +42,20 @@ static int min_int(int a, int b) {
return a < b ? a : b;
}
+static Window get_focused_window(Display *display, Atom net_active_window_atom) {
+ Atom type;
+ int format = 0;
+ unsigned long num_items = 0;
+ unsigned long bytes_after = 0;
+ unsigned char *properties = NULL;
+ if(XGetWindowProperty(display, DefaultRootWindow(display), net_active_window_atom, 0, 1024, False, AnyPropertyType, &type, &format, &num_items, &bytes_after, &properties) == Success && properties) {
+ Window focused_window = *(unsigned long*)properties;
+ XFree(properties);
+ return focused_window;
+ }
+ return None;
+}
+
static void gsr_capture_xcomposite_cuda_stop(gsr_capture *cap, AVCodecContext *video_codec_context);
static bool cuda_register_opengl_texture(gsr_capture_xcomposite_cuda *cap_xcomp) {
@@ -58,6 +72,7 @@ static bool cuda_register_opengl_texture(gsr_capture_xcomposite_cuda *cap_xcomp)
"Error: cuGraphicsGLRegisterImage failed, error %s, texture "
"id: %u\n",
err_str, cap_xcomp->target_texture_id);
+ res = cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx);
return false;
}
@@ -122,13 +137,6 @@ static bool cuda_create_codec_context(gsr_capture_xcomposite_cuda *cap_xcomp, AV
}
static unsigned int gl_create_texture(gsr_capture_xcomposite_cuda *cap_xcomp, int width, int height) {
- // Generating this second texture is needed because
- // cuGraphicsGLRegisterImage cant be used with the texture that is mapped
- // directly to the pixmap.
- // TODO: Investigate if it's somehow possible to use the pixmap texture
- // directly, this should improve performance since only less image copy is
- // then needed every frame.
- // Ignoring failure for now.. TODO: Show proper error
unsigned int texture_id = 0;
cap_xcomp->egl.glGenTextures(1, &texture_id);
cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, texture_id);
@@ -136,8 +144,8 @@ static unsigned int gl_create_texture(gsr_capture_xcomposite_cuda *cap_xcomp, in
cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, 0);
return texture_id;
@@ -146,18 +154,34 @@ static unsigned int gl_create_texture(gsr_capture_xcomposite_cuda *cap_xcomp, in
static int gsr_capture_xcomposite_cuda_start(gsr_capture *cap, AVCodecContext *video_codec_context) {
gsr_capture_xcomposite_cuda *cap_xcomp = cap->priv;
+ if(cap_xcomp->params.follow_focused) {
+ cap_xcomp->net_active_window_atom = XInternAtom(cap_xcomp->dpy, "_NET_ACTIVE_WINDOW", False);
+ if(!cap_xcomp->net_active_window_atom) {
+ fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_start failed: failed to get _NET_ACTIVE_WINDOW atom\n");
+ return -1;
+ }
+ cap_xcomp->window = get_focused_window(cap_xcomp->dpy, cap_xcomp->net_active_window_atom);
+ } else {
+ cap_xcomp->window = cap_xcomp->params.window;
+ }
+
+ /* TODO: Do these in tick, and allow error if follow_focused */
+
XWindowAttributes attr;
- if(!XGetWindowAttributes(cap_xcomp->dpy, cap_xcomp->params.window, &attr)) {
- fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_start failed: invalid window id: %lu\n", cap_xcomp->params.window);
+ attr.width = 0;
+ attr.height = 0;
+ if(!XGetWindowAttributes(cap_xcomp->dpy, cap_xcomp->window, &attr) && !cap_xcomp->params.follow_focused) {
+ fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_start failed: invalid window id: %lu\n", cap_xcomp->window);
return -1;
}
cap_xcomp->window_size.x = max_int(attr.width, 0);
cap_xcomp->window_size.y = max_int(attr.height, 0);
- Window c;
- XTranslateCoordinates(cap_xcomp->dpy, cap_xcomp->params.window, DefaultRootWindow(cap_xcomp->dpy), 0, 0, &cap_xcomp->window_pos.x, &cap_xcomp->window_pos.y, &c);
- XSelectInput(cap_xcomp->dpy, cap_xcomp->params.window, StructureNotifyMask | ExposureMask);
+ if(cap_xcomp->params.follow_focused)
+ XSelectInput(cap_xcomp->dpy, DefaultRootWindow(cap_xcomp->dpy), PropertyChangeMask);
+
+ XSelectInput(cap_xcomp->dpy, cap_xcomp->window, StructureNotifyMask | ExposureMask);
if(!gsr_egl_load(&cap_xcomp->egl, cap_xcomp->dpy)) {
fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_start: failed to load opengl\n");
@@ -165,16 +189,16 @@ static int gsr_capture_xcomposite_cuda_start(gsr_capture *cap, AVCodecContext *v
}
cap_xcomp->egl.eglSwapInterval(cap_xcomp->egl.egl_display, 0);
- // TODO: Fallback to composite window
- if(window_texture_init(&cap_xcomp->window_texture, cap_xcomp->dpy, cap_xcomp->params.window, &cap_xcomp->egl) != 0) {
- fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_start: failed get window texture for window %ld\n", cap_xcomp->params.window);
+ if(window_texture_init(&cap_xcomp->window_texture, cap_xcomp->dpy, cap_xcomp->window, &cap_xcomp->egl) != 0 && !cap_xcomp->params.follow_focused) {
+ fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_start: failed get window texture for window %ld\n", cap_xcomp->window);
gsr_egl_unload(&cap_xcomp->egl);
return -1;
}
- cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&cap_xcomp->window_texture));
cap_xcomp->texture_size.x = 0;
cap_xcomp->texture_size.y = 0;
+
+ cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&cap_xcomp->window_texture));
cap_xcomp->egl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &cap_xcomp->texture_size.x);
cap_xcomp->egl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &cap_xcomp->texture_size.y);
cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, 0);
@@ -182,16 +206,21 @@ static int gsr_capture_xcomposite_cuda_start(gsr_capture *cap, AVCodecContext *v
cap_xcomp->texture_size.x = max_int(2, cap_xcomp->texture_size.x & ~1);
cap_xcomp->texture_size.y = max_int(2, cap_xcomp->texture_size.y & ~1);
- cap_xcomp->target_texture_id = gl_create_texture(cap_xcomp, cap_xcomp->texture_size.x, cap_xcomp->texture_size.y);
+ video_codec_context->width = cap_xcomp->texture_size.x;
+ video_codec_context->height = cap_xcomp->texture_size.y;
+
+ if(cap_xcomp->params.region_size.x > 0 && cap_xcomp->params.region_size.y) {
+ video_codec_context->width = cap_xcomp->params.region_size.x;
+ video_codec_context->height = cap_xcomp->params.region_size.y;
+ }
+
+ cap_xcomp->target_texture_id = gl_create_texture(cap_xcomp, video_codec_context->width, video_codec_context->height);
if(cap_xcomp->target_texture_id == 0) {
fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_start: failed to create opengl texture\n");
gsr_capture_xcomposite_cuda_stop(cap, video_codec_context);
return -1;
}
- video_codec_context->width = cap_xcomp->texture_size.x;
- video_codec_context->height = cap_xcomp->texture_size.y;
-
if(!gsr_cuda_load(&cap_xcomp->cuda)) {
gsr_capture_xcomposite_cuda_stop(cap, video_codec_context);
return -1;
@@ -221,13 +250,11 @@ static void gsr_capture_xcomposite_cuda_stop(gsr_capture *cap, AVCodecContext *v
cap_xcomp->target_texture_id = 0;
}
- if(cap_xcomp->composite_window) {
- XCompositeUnredirectWindow(cap_xcomp->dpy, cap_xcomp->composite_window, CompositeRedirectAutomatic);
- cap_xcomp->composite_window = None;
- }
-
- av_buffer_unref(&video_codec_context->hw_device_ctx);
- av_buffer_unref(&video_codec_context->hw_frames_ctx);
+ if(video_codec_context->hw_device_ctx)
+ av_buffer_unref(&video_codec_context->hw_device_ctx);
+ // Not needed because the above call to unref device ctx also frees this?
+ //if(video_codec_context->hw_frames_ctx)
+ // av_buffer_unref(&video_codec_context->hw_frames_ctx);
if(cap_xcomp->cuda.cu_ctx) {
CUcontext old_ctx;
@@ -241,7 +268,6 @@ static void gsr_capture_xcomposite_cuda_stop(gsr_capture *cap, AVCodecContext *v
gsr_egl_unload(&cap_xcomp->egl);
if(cap_xcomp->dpy) {
- // TODO: Why is this crashing?
XCloseDisplay(cap_xcomp->dpy);
cap_xcomp->dpy = NULL;
}
@@ -268,22 +294,18 @@ static void gsr_capture_xcomposite_cuda_tick(gsr_capture *cap, AVCodecContext *v
cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx);
}
- if(XCheckTypedWindowEvent(cap_xcomp->dpy, cap_xcomp->params.window, DestroyNotify, &cap_xcomp->xev)) {
+ if(!cap_xcomp->params.follow_focused && XCheckTypedWindowEvent(cap_xcomp->dpy, cap_xcomp->window, DestroyNotify, &cap_xcomp->xev)) {
cap_xcomp->should_stop = true;
cap_xcomp->stop_is_error = false;
}
- if(XCheckTypedWindowEvent(cap_xcomp->dpy, cap_xcomp->params.window, Expose, &cap_xcomp->xev) && cap_xcomp->xev.xexpose.count == 0) {
+ if(XCheckTypedWindowEvent(cap_xcomp->dpy, cap_xcomp->window, Expose, &cap_xcomp->xev) && cap_xcomp->xev.xexpose.count == 0) {
cap_xcomp->window_resize_timer = clock_get_monotonic_seconds();
cap_xcomp->window_resized = true;
}
- if(XCheckTypedWindowEvent(cap_xcomp->dpy, cap_xcomp->params.window, ConfigureNotify, &cap_xcomp->xev) && cap_xcomp->xev.xconfigure.window == cap_xcomp->params.window) {
- while(XCheckTypedWindowEvent(cap_xcomp->dpy, cap_xcomp->params.window, ConfigureNotify, &cap_xcomp->xev)) {}
- Window c;
- XTranslateCoordinates(cap_xcomp->dpy, cap_xcomp->params.window, DefaultRootWindow(cap_xcomp->dpy), 0, 0, &cap_xcomp->xev.xconfigure.x, &cap_xcomp->xev.xconfigure.y, &c);
- cap_xcomp->window_pos.x = cap_xcomp->xev.xconfigure.x;
- cap_xcomp->window_pos.y = cap_xcomp->xev.xconfigure.y;
+ if(XCheckTypedWindowEvent(cap_xcomp->dpy, cap_xcomp->window, ConfigureNotify, &cap_xcomp->xev) && cap_xcomp->xev.xconfigure.window == cap_xcomp->window) {
+ while(XCheckTypedWindowEvent(cap_xcomp->dpy, cap_xcomp->window, ConfigureNotify, &cap_xcomp->xev)) {}
/* Window resize */
if(cap_xcomp->xev.xconfigure.width != cap_xcomp->window_size.x || cap_xcomp->xev.xconfigure.height != cap_xcomp->window_size.y) {
@@ -294,10 +316,31 @@ static void gsr_capture_xcomposite_cuda_tick(gsr_capture *cap, AVCodecContext *v
}
}
+ if(cap_xcomp->params.follow_focused && XCheckTypedWindowEvent(cap_xcomp->dpy, DefaultRootWindow(cap_xcomp->dpy), PropertyNotify, &cap_xcomp->xev) && cap_xcomp->xev.xproperty.atom == cap_xcomp->net_active_window_atom) {
+ Window focused_window = get_focused_window(cap_xcomp->dpy, cap_xcomp->net_active_window_atom);
+ if(focused_window != cap_xcomp->window) {
+ XSelectInput(cap_xcomp->dpy, cap_xcomp->window, 0);
+ cap_xcomp->window = focused_window;
+ XSelectInput(cap_xcomp->dpy, cap_xcomp->window, StructureNotifyMask | ExposureMask);
+
+ XWindowAttributes attr;
+ attr.width = 0;
+ attr.height = 0;
+ if(!XGetWindowAttributes(cap_xcomp->dpy, cap_xcomp->window, &attr))
+ fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_start failed: invalid window id: %lu\n", cap_xcomp->window);
+
+ cap_xcomp->window_size.x = max_int(attr.width, 0);
+ cap_xcomp->window_size.y = max_int(attr.height, 0);
+ cap_xcomp->window_resized = true;
+
+ window_texture_deinit(&cap_xcomp->window_texture);
+ window_texture_init(&cap_xcomp->window_texture, cap_xcomp->dpy, cap_xcomp->window, &cap_xcomp->egl); // TODO: Do not do the below window_texture_on_resize after this
+ }
+ }
+
const double window_resize_timeout = 1.0; // 1 second
if(cap_xcomp->window_resized && clock_get_monotonic_seconds() - cap_xcomp->window_resize_timer >= window_resize_timeout) {
cap_xcomp->window_resized = false;
- fprintf(stderr, "Resize window!\n");
if(window_texture_on_resize(&cap_xcomp->window_texture) != 0) {
fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_tick: window_texture_on_resize failed\n");
cap_xcomp->should_stop = true;
@@ -305,9 +348,10 @@ static void gsr_capture_xcomposite_cuda_tick(gsr_capture *cap, AVCodecContext *v
return;
}
- cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&cap_xcomp->window_texture));
cap_xcomp->texture_size.x = 0;
cap_xcomp->texture_size.y = 0;
+
+ cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&cap_xcomp->window_texture));
cap_xcomp->egl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &cap_xcomp->texture_size.x);
cap_xcomp->egl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &cap_xcomp->texture_size.y);
cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, 0);
@@ -315,37 +359,18 @@ static void gsr_capture_xcomposite_cuda_tick(gsr_capture *cap, AVCodecContext *v
cap_xcomp->texture_size.x = min_int(video_codec_context->width, max_int(2, cap_xcomp->texture_size.x & ~1));
cap_xcomp->texture_size.y = min_int(video_codec_context->height, max_int(2, cap_xcomp->texture_size.y & ~1));
- cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, cap_xcomp->target_texture_id);
- cap_xcomp->egl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, cap_xcomp->texture_size.x, cap_xcomp->texture_size.y, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
- cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, 0);
-
- CUcontext old_ctx;
- CUresult res = cap_xcomp->cuda.cuCtxPushCurrent_v2(cap_xcomp->cuda.cu_ctx);
-
- cap_xcomp->cuda.cuGraphicsUnmapResources(1, &cap_xcomp->cuda_graphics_resource, 0);
- cap_xcomp->cuda.cuGraphicsUnregisterResource(cap_xcomp->cuda_graphics_resource);
- res = cap_xcomp->cuda.cuGraphicsGLRegisterImage(&cap_xcomp->cuda_graphics_resource, cap_xcomp->target_texture_id, GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY);
- if (res != CUDA_SUCCESS) {
- const char *err_str = "unknown";
- cap_xcomp->cuda.cuGetErrorString(res, &err_str);
- fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_tick: cuGraphicsGLRegisterImage failed, error %s, texture id: %u\n", err_str, cap_xcomp->target_texture_id);
- cap_xcomp->should_stop = true;
- cap_xcomp->stop_is_error = true;
- res = cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx);
- return;
+ if(!cap_xcomp->params.follow_focused) {
+ cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, cap_xcomp->target_texture_id);
+ cap_xcomp->egl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, cap_xcomp->texture_size.x, cap_xcomp->texture_size.y, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, 0);
}
- res = cap_xcomp->cuda.cuGraphicsResourceSetMapFlags(cap_xcomp->cuda_graphics_resource, CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
- res = cap_xcomp->cuda.cuGraphicsMapResources(1, &cap_xcomp->cuda_graphics_resource, 0);
- res = cap_xcomp->cuda.cuGraphicsSubResourceGetMappedArray(&cap_xcomp->mapped_array, cap_xcomp->cuda_graphics_resource, 0, 0);
-
av_frame_free(frame);
*frame = av_frame_alloc();
if(!frame) {
fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_tick: failed to allocate frame\n");
cap_xcomp->should_stop = true;
cap_xcomp->stop_is_error = true;
- res = cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx);
return;
}
(*frame)->format = video_codec_context->pix_fmt;
@@ -357,14 +382,12 @@ static void gsr_capture_xcomposite_cuda_tick(gsr_capture *cap, AVCodecContext *v
fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_tick: av_hwframe_get_buffer failed\n");
cap_xcomp->should_stop = true;
cap_xcomp->stop_is_error = true;
- res = cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx);
return;
}
- // Make it completely black to clear unused parts
- // TODO: cuMemsetD32?
- res = cap_xcomp->cuda.cuMemsetD8_v2((CUdeviceptr)(*frame)->data[0], 0, (*frame)->width * (*frame)->height * 4);
- res = cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx);
+ // Clear texture with black background because the source texture (window_texture_get_opengl_texture_id(&cap_xcomp->window_texture))
+ // might be smaller than cap_xcomp->target_texture_id
+ cap_xcomp->egl.glClearTexImage(cap_xcomp->target_texture_id, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
}
}
@@ -384,25 +407,25 @@ static bool gsr_capture_xcomposite_cuda_should_stop(gsr_capture *cap, bool *err)
static int gsr_capture_xcomposite_cuda_capture(gsr_capture *cap, AVFrame *frame) {
gsr_capture_xcomposite_cuda *cap_xcomp = cap->priv;
- // TODO: Use a framebuffer instead. glCopyImageSubData requires opengl 4.2
vec2i source_pos = { 0, 0 };
vec2i source_size = cap_xcomp->texture_size;
- // Requires opengl 4.2... TODO: Replace with earlier opengl if opengl < 4.2.
- cap_xcomp->egl.glCopyImageSubData(
- window_texture_get_opengl_texture_id(&cap_xcomp->window_texture), GL_TEXTURE_2D, 0, source_pos.x, source_pos.y, 0,
- cap_xcomp->target_texture_id, GL_TEXTURE_2D, 0, 0, 0, 0,
- source_size.x, source_size.y, 1);
- unsigned int err = cap_xcomp->egl.glGetError();
- if(err != 0) {
- static bool error_shown = false;
- if(!error_shown) {
- error_shown = true;
- fprintf(stderr, "Error: glCopyImageSubData failed, gl error: %d\n", err);
+ if(cap_xcomp->window_texture.texture_id != 0) {
+ /* TODO: Remove this copy, which is only possible by using nvenc directly and encoding window_pixmap.target_texture_id */
+ cap_xcomp->egl.glCopyImageSubData(
+ window_texture_get_opengl_texture_id(&cap_xcomp->window_texture), GL_TEXTURE_2D, 0, source_pos.x, source_pos.y, 0,
+ cap_xcomp->target_texture_id, GL_TEXTURE_2D, 0, 0, 0, 0,
+ source_size.x, source_size.y, 1);
+ unsigned int err = cap_xcomp->egl.glGetError();
+ if(err != 0) {
+ static bool error_shown = false;
+ if(!error_shown) {
+ error_shown = true;
+ fprintf(stderr, "Error: glCopyImageSubData failed, gl error: %d\n", err);
+ }
}
}
cap_xcomp->egl.eglSwapBuffers(cap_xcomp->egl.egl_display, cap_xcomp->egl.egl_surface);
- // TODO: Remove this copy, which is only possible by using nvenc directly and encoding window_pixmap.target_texture_id
frame->linesize[0] = frame->width * 4;
@@ -426,8 +449,8 @@ static int gsr_capture_xcomposite_cuda_capture(gsr_capture *cap, AVFrame *frame)
}
static void gsr_capture_xcomposite_cuda_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) {
- gsr_capture_xcomposite_cuda_stop(cap, video_codec_context);
if(cap->priv) {
+ gsr_capture_xcomposite_cuda_stop(cap, video_codec_context);
free(cap->priv);
cap->priv = NULL;
}
diff --git a/src/capture/xcomposite_drm.c b/src/capture/xcomposite_drm.c
index 65dec1e..4c14769 100644
--- a/src/capture/xcomposite_drm.c
+++ b/src/capture/xcomposite_drm.c
@@ -121,8 +121,8 @@ static unsigned int gl_create_texture(gsr_capture_xcomposite_drm *cap_xcomp, int
cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ cap_xcomp->egl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, 0);
return texture_id;
@@ -403,6 +403,18 @@ static int gsr_capture_xcomposite_drm_start(gsr_capture *cap, AVCodecContext *vi
return -1;
}
+ if(!cap_xcomp->egl.eglExportDMABUFImageQueryMESA) {
+ fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: could not find eglExportDMABUFImageQueryMESA\n");
+ gsr_egl_unload(&cap_xcomp->egl);
+ return -1;
+ }
+
+ if(!cap_xcomp->egl.eglExportDMABUFImageMESA) {
+ fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: could not find eglExportDMABUFImageMESA\n");
+ gsr_egl_unload(&cap_xcomp->egl);
+ return -1;
+ }
+
/* Disable vsync */
cap_xcomp->egl.eglSwapInterval(cap_xcomp->egl.egl_display, 0);
#if 0
@@ -589,6 +601,8 @@ static void free_desc(void *opaque, uint8_t *data) {
static void gsr_capture_xcomposite_drm_tick(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame **frame) {
gsr_capture_xcomposite_drm *cap_xcomp = cap->priv;
+ cap_xcomp->egl.glClear(GL_COLOR_BUFFER_BIT);
+
if(!cap_xcomp->created_hw_frame) {
cap_xcomp->created_hw_frame = true;
@@ -702,9 +716,11 @@ static void gsr_capture_xcomposite_drm_tick(gsr_capture *cap, AVCodecContext *vi
if(res < 0) {
fprintf(stderr, "av_hwframe_map failed: %d\n", res);
}
- }
- cap_xcomp->egl.glClear(GL_COLOR_BUFFER_BIT);
+ // Clear texture with black background because the source texture (window_texture_get_opengl_texture_id(&cap_xcomp->window_texture))
+ // might be smaller than cap_xcomp->target_texture_id
+ cap_xcomp->egl.glClearTexImage(cap_xcomp->target_texture_id, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ }
}
static bool gsr_capture_xcomposite_drm_should_stop(gsr_capture *cap, bool *err) {
@@ -764,7 +780,7 @@ static int gsr_capture_xcomposite_drm_capture(gsr_capture *cap, AVFrame *frame)
vec2i source_size = cap_xcomp->texture_size;
#if 1
- // Requires opengl 4.2... TODO: Replace with earlier opengl if opengl < 4.2.
+ /* TODO: Remove this copy, which is only possible by using nvenc directly and encoding window_pixmap.target_texture_id */
cap_xcomp->egl.glCopyImageSubData(
window_texture_get_opengl_texture_id(&cap_xcomp->window_texture), GL_TEXTURE_2D, 0, 0, 0, 0,
cap_xcomp->target_texture_id, GL_TEXTURE_2D, 0, 0, 0, 0,
@@ -808,6 +824,7 @@ static int gsr_capture_xcomposite_drm_capture(gsr_capture *cap, AVFrame *frame)
}
static void gsr_capture_xcomposite_drm_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) {
+ (void)video_codec_context;
if(cap->priv) {
free(cap->priv);
cap->priv = NULL;
diff --git a/src/egl.c b/src/egl.c
index 05819c2..dbc8928 100644
--- a/src/egl.c
+++ b/src/egl.c
@@ -3,11 +3,12 @@
#include <string.h>
static bool gsr_egl_create_window(gsr_egl *self) {
- EGLDisplay egl_display = NULL;
EGLConfig ecfg;
- int32_t num_config;
- EGLSurface egl_surface;
- EGLContext egl_context;
+ int32_t num_config = 0;
+ EGLDisplay egl_display = NULL;
+ EGLSurface egl_surface = NULL;
+ EGLContext egl_context = NULL;
+ Window window = None;
int32_t attr[] = {
EGL_BUFFER_SIZE, 24,
@@ -21,8 +22,7 @@ static bool gsr_egl_create_window(gsr_egl *self) {
EGL_NONE
};
- // TODO: Is there a way to remove the need to create a window?
- Window window = XCreateWindow(self->dpy, DefaultRootWindow(self->dpy), 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
+ window = XCreateWindow(self->dpy, DefaultRootWindow(self->dpy), 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
if(!window) {
fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to create gl window\n");
@@ -32,40 +32,32 @@ static bool gsr_egl_create_window(gsr_egl *self) {
egl_display = self->eglGetDisplay(self->dpy);
if(!egl_display) {
fprintf(stderr, "gsr error: gsr_egl_create_window failed: eglGetDisplay failed\n");
- return false;
+ goto fail;
}
if(!self->eglInitialize(egl_display, NULL, NULL)) {
fprintf(stderr, "gsr error: gsr_egl_create_window failed: eglInitialize failed\n");
- return false;
- }
-
- // TODO: Cleanup ecfg?
- if (!self->eglChooseConfig( egl_display, attr, &ecfg, 1, &num_config ) ) {
- //cerr << "Failed to choose config (eglError: " << eglGetError() << ")" << endl;
- return false;
+ goto fail;
}
- if ( num_config != 1 ) {
- //cerr << "Didn't get exactly one config, but " << num_config << endl;
- return false;
+ if(!self->eglChooseConfig(egl_display, attr, &ecfg, 1, &num_config) || num_config != 1) {
+ fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to find a matching config\n");
+ goto fail;
}
- egl_surface = self->eglCreateWindowSurface ( egl_display, ecfg, (EGLNativeWindowType)window, NULL );
- if ( !egl_surface ) {
- //cerr << "Unable to create EGL surface (eglError: " << eglGetError() << ")" << endl;
- return false;
+ egl_surface = self->eglCreateWindowSurface(egl_display, ecfg, (EGLNativeWindowType)window, NULL);
+ if(!egl_surface) {
+ fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create window surface\n");
+ goto fail;
}
- //// egl-contexts collect all state descriptions needed required for operation
- egl_context = self->eglCreateContext ( egl_display, ecfg, NULL, ctxattr );
- if ( !egl_context ) {
- //cerr << "Unable to create EGL context (eglError: " << eglGetError() << ")" << endl;
- return false;
+ egl_context = self->eglCreateContext(egl_display, ecfg, NULL, ctxattr);
+ if(!egl_context) {
+ fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create egl context\n");
+ goto fail;
}
-
- //// associate the egl-context with the egl-surface
- self->eglMakeCurrent( egl_display, egl_surface, egl_surface, egl_context );
+
+ self->eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
self->egl_display = egl_display;
self->egl_surface = egl_surface;
@@ -74,31 +66,31 @@ static bool gsr_egl_create_window(gsr_egl *self) {
return true;
fail:
- // TODO:
- /*
+ if(egl_context)
+ self->eglDestroyContext(egl_display, egl_context);
+ if(egl_surface)
+ self->eglDestroySurface(egl_display, egl_surface);
+ if(egl_display)
+ self->eglTerminate(egl_display);
if(window)
XDestroyWindow(self->dpy, window);
- if(colormap)
- XFreeColormap(self->dpy, colormap);
- if(gl_context)
- self->glXDestroyContext(self->dpy, gl_context);
- if(visual_info)
- XFree(visual_info);
- XFree(fbconfigs);
- */
- return False;
+ return false;
}
static bool gsr_egl_load_egl(gsr_egl *self, void *library) {
dlsym_assign required_dlsym[] = {
{ (void**)&self->eglGetDisplay, "eglGetDisplay" },
{ (void**)&self->eglInitialize, "eglInitialize" },
+ { (void**)&self->eglTerminate, "eglTerminate" },
{ (void**)&self->eglChooseConfig, "eglChooseConfig" },
{ (void**)&self->eglCreateWindowSurface, "eglCreateWindowSurface" },
{ (void**)&self->eglCreateContext, "eglCreateContext" },
{ (void**)&self->eglMakeCurrent, "eglMakeCurrent" },
{ (void**)&self->eglCreatePixmapSurface, "eglCreatePixmapSurface" },
- { (void**)&self->eglCreateImage, "eglCreateImage" }, // TODO: eglCreateImageKHR
+ { (void**)&self->eglCreateImage, "eglCreateImage" }, /* TODO: use eglCreateImageKHR instead? */
+ { (void**)&self->eglDestroyContext, "eglDestroyContext" },
+ { (void**)&self->eglDestroySurface, "eglDestroySurface" },
+ { (void**)&self->eglDestroyImage, "eglDestroyImage" },
{ (void**)&self->eglBindTexImage, "eglBindTexImage" },
{ (void**)&self->eglSwapInterval, "eglSwapInterval" },
{ (void**)&self->eglSwapBuffers, "eglSwapBuffers" },
@@ -115,23 +107,13 @@ static bool gsr_egl_load_egl(gsr_egl *self, void *library) {
return true;
}
-static bool gsr_egl_mesa_load_egl(gsr_egl *self) {
+static bool gsr_egl_proc_load_egl(gsr_egl *self) {
self->eglExportDMABUFImageQueryMESA = self->eglGetProcAddress("eglExportDMABUFImageQueryMESA");
self->eglExportDMABUFImageMESA = self->eglGetProcAddress("eglExportDMABUFImageMESA");
self->glEGLImageTargetTexture2DOES = self->eglGetProcAddress("glEGLImageTargetTexture2DOES");
- if(!self->eglExportDMABUFImageQueryMESA) {
- fprintf(stderr, "could not find eglExportDMABUFImageQueryMESA\n");
- return false;
- }
-
- if(!self->eglExportDMABUFImageMESA) {
- fprintf(stderr, "could not find eglExportDMABUFImageMESA\n");
- return false;
- }
-
if(!self->glEGLImageTargetTexture2DOES) {
- fprintf(stderr, "could not find glEGLImageTargetTexture2DOES\n");
+ fprintf(stderr, "gsr error: gsr_egl_load failed: could not find glEGLImageTargetTexture2DOES\n");
return false;
}
@@ -151,6 +133,7 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
{ (void**)&self->glGetTexLevelParameteriv, "glGetTexLevelParameteriv" },
{ (void**)&self->glTexImage2D, "glTexImage2D" },
{ (void**)&self->glCopyImageSubData, "glCopyImageSubData" },
+ { (void**)&self->glClearTexImage, "glClearTexImage" },
{ (void**)&self->glGenFramebuffers, "glGenFramebuffers" },
{ (void**)&self->glBindFramebuffer, "glBindFramebuffer" },
{ (void**)&self->glViewport, "glViewport" },
@@ -228,7 +211,7 @@ bool gsr_egl_load(gsr_egl *self, Display *dpy) {
return false;
}
- if(!gsr_egl_mesa_load_egl(self)) {
+ if(!gsr_egl_proc_load_egl(self)) {
dlclose(egl_lib);
dlclose(gl_lib);
memset(self, 0, sizeof(gsr_egl));
@@ -247,14 +230,21 @@ bool gsr_egl_load(gsr_egl *self, Display *dpy) {
return true;
}
-bool gsr_egl_make_context_current(gsr_egl *self) {
- // TODO:
- return true;
- //return self->glXMakeContextCurrent(self->dpy, self->window, self->window, self->gl_context);
-}
-
void gsr_egl_unload(gsr_egl *self) {
- // TODO: Cleanup of egl resources
+ if(self->egl_context) {
+ self->eglDestroyContext(self->egl_display, self->egl_context);
+ self->egl_context = NULL;
+ }
+
+ if(self->egl_surface) {
+ self->eglDestroySurface(self->egl_display, self->egl_surface);
+ self->egl_surface = NULL;
+ }
+
+ if(self->egl_display) {
+ self->eglTerminate(self->egl_display);
+ self->egl_display = NULL;
+ }
if(self->window) {
XDestroyWindow(self->dpy, self->window);
diff --git a/src/main.cpp b/src/main.cpp
index bffa93b..bde09b2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -38,6 +38,12 @@ extern "C" {
#include <deque>
#include <future>
+typedef enum {
+ GPU_VENDOR_AMD,
+ GPU_VENDOR_INTEL,
+ GPU_VENDOR_NVIDIA
+} gpu_vendor;
+
// TODO: Remove LIBAVUTIL_VERSION_MAJOR checks in the future when ubuntu, pop os LTS etc update ffmpeg to >= 5.0
static const int VIDEO_STREAM_INDEX = 0;
@@ -322,70 +328,51 @@ static AVCodecContext *create_video_codec_context(AVPixelFormat pix_fmt,
return codec_context;
}
-#if 0
-static const AVCodec* find_h264_encoder() {
- const AVCodec *codec = avcodec_find_encoder_by_name("h264_vaapi");
- if(!codec)
- codec = avcodec_find_encoder_by_name("vaapi_h264");
- return codec;
+static bool check_if_codec_valid_for_hardware(const AVCodec *codec) {
+ bool success = false;
+ // Do not use AV_PIX_FMT_CUDA because we dont want to do full check with hardware context
+ AVCodecContext *codec_context = create_video_codec_context(AV_PIX_FMT_YUV420P, VideoQuality::VERY_HIGH, 60, codec, false);
+ codec_context->width = 1920;
+ codec_context->height = 1080;
+ if(codec_context) {
+ success = avcodec_open2(codec_context, codec_context->codec, NULL) == 0;
+ avcodec_free_context(&codec_context);
+ }
+ return success;
}
-static const AVCodec* find_h265_encoder() {
- const AVCodec *codec = avcodec_find_encoder_by_name("hevc_vaapi");
+static const AVCodec* find_h264_encoder(gpu_vendor vendor) {
+ const AVCodec *codec = avcodec_find_encoder_by_name(vendor == GPU_VENDOR_NVIDIA ? "h264_nvenc" : "h264_vaapi");
if(!codec)
- codec = avcodec_find_encoder_by_name("vaapi_hevc");
- return codec;
-}
-#else
-static const AVCodec* find_h264_encoder() {
- const AVCodec *codec = avcodec_find_encoder_by_name("h264_nvenc");
- if(!codec)
- codec = avcodec_find_encoder_by_name("nvenc_h264");
+ codec = avcodec_find_encoder_by_name(vendor == GPU_VENDOR_NVIDIA ? "nvenc_h264" : "vaapi_h264");
static bool checked = false;
+ static bool checked_success = true;
if(!checked) {
checked = true;
- // Do not use AV_PIX_FMT_CUDA because we dont want to do full check with hardware context
- AVCodecContext *codec_context = create_video_codec_context(AV_PIX_FMT_YUV420P, VideoQuality::VERY_HIGH, 60, codec, false);
- codec_context->width = 1920;
- codec_context->height = 1080;
- if(codec_context) {
- if (avcodec_open2(codec_context, codec_context->codec, NULL) < 0) {
- avcodec_free_context(&codec_context);
- return nullptr;
- }
- avcodec_free_context(&codec_context);
- }
+ if(!check_if_codec_valid_for_hardware(codec))
+ checked_success = false;
}
- return codec;
+ return checked_success ? codec : nullptr;
}
-static const AVCodec* find_h265_encoder() {
- const AVCodec *codec = avcodec_find_encoder_by_name("hevc_nvenc");
+static const AVCodec* find_h265_encoder(gpu_vendor vendor) {
+ const AVCodec *codec = avcodec_find_encoder_by_name(vendor == GPU_VENDOR_NVIDIA ? "hevc_nvenc" : "hevc_vaapi");
if(!codec)
- codec = avcodec_find_encoder_by_name("nvenc_hevc");
+ codec = avcodec_find_encoder_by_name(vendor == GPU_VENDOR_NVIDIA ? "nvenc_hevc" : "vaapi_hevc");
if(!codec)
return nullptr;
static bool checked = false;
+ static bool checked_success = true;
if(!checked) {
checked = true;
- // Do not use AV_PIX_FMT_CUDA because we dont want to do full check with hardware context
- AVCodecContext *codec_context = create_video_codec_context(AV_PIX_FMT_YUV420P, VideoQuality::VERY_HIGH, 60, codec, false);
- codec_context->width = 1920;
- codec_context->height = 1080;
- if(codec_context) {
- if (avcodec_open2(codec_context, codec_context->codec, NULL) < 0) {
- avcodec_free_context(&codec_context);
- return nullptr;
- }
- avcodec_free_context(&codec_context);
- }
+ if(!check_if_codec_valid_for_hardware(codec))
+ checked_success = false;
}
- return codec;
+ return checked_success ? codec : nullptr;
}
-#endif
static AVFrame* open_audio(AVCodecContext *audio_codec_context) {
int ret;
@@ -499,11 +486,12 @@ static void open_video(AVCodecContext *codec_context, VideoQuality video_quality
}
static void usage() {
- fprintf(stderr, "usage: gpu-screen-recorder -w <window_id> [-c <container_format>] -f <fps> [-a <audio_input>...] [-q <quality>] [-r <replay_buffer_size_sec>] [-o <output_file>]\n");
+ fprintf(stderr, "usage: gpu-screen-recorder -w <window_id|monitor|focused> [-c <container_format>] [-s WxH] -f <fps> [-a <audio_input>...] [-q <quality>] [-r <replay_buffer_size_sec>] [-o <output_file>]\n");
fprintf(stderr, "OPTIONS:\n");
- fprintf(stderr, " -w Window to record or a display, \"screen\" or \"screen-direct\". The display is the display name in xrandr and if \"screen\" or \"screen-direct\" is selected then all displays are recorded and they are recorded in h265 (aka hevc)."
+ fprintf(stderr, " -w Window to record, a display, \"screen\", \"screen-direct\" or \"focused\". The display is the display (monitor) name in xrandr and if \"screen\" or \"screen-direct\" is selected then all displays are recorded. If this is \"focused\" then the currently focused window is recorded. When recording the focused window then the -s option has to be used as well.\n"
"\"screen-direct\" skips one texture copy for fullscreen applications so it may lead to better performance and it works with VRR monitors when recording fullscreen application but may break some applications, such as mpv in fullscreen mode. Recording a display requires a gpu with NvFBC support.\n");
fprintf(stderr, " -c Container format for output file, for example mp4, or flv. Only required if no output file is specified or if recording in replay buffer mode. If an output file is specified and -c is not used then the container format is determined from the output filename extension.\n");
+ fprintf(stderr, " -s The size (area) to record at in the format WxH, for example 1920x1080. This option is only supported (and required) when -w is \"focused\".\n");
fprintf(stderr, " -f Framerate to record at.\n");
fprintf(stderr, " -a Audio device to record from (pulse audio device). Can be specified multiple times. Each time this is specified a new audio track is added for the specified audio device. A name can be given to the audio input device by prefixing the audio input with <name>/, for example \"dummy/alsa_output.pci-0000_00_1b.0.analog-stereo.monitor\". Optional, no audio track is added by default.\n");
fprintf(stderr, " -q Video quality. Should be either 'medium', 'high', 'very_high' or 'ultra'. 'high' is the recommended option when live streaming or when you have a slower harddrive. Optional, set to 'very_high' be default.\n");
@@ -732,12 +720,6 @@ static bool is_livestream_path(const char *str) {
return false;
}
-typedef enum {
- GPU_VENDOR_AMD,
- GPU_VENDOR_INTEL,
- GPU_VENDOR_NVIDIA
-} gpu_vendor;
-
typedef struct {
gpu_vendor vendor;
int gpu_version; /* 0 if unknown */
@@ -792,10 +774,9 @@ int main(int argc, char **argv) {
std::map<std::string, Arg> args = {
{ "-w", Arg { {}, false, false } },
- //{ "-s", Arg { nullptr, true } },
{ "-c", Arg { {}, true, false } },
{ "-f", Arg { {}, false, false } },
- //{ "-s", Arg { {}, true, false } },
+ { "-s", Arg { {}, true, false } },
{ "-a", Arg { {}, true, true } },
{ "-q", Arg { {}, true, false } },
{ "-o", Arg { {}, true, false } },
@@ -927,12 +908,73 @@ int main(int argc, char **argv) {
very_old_gpu = true;
}
+ // TODO: Remove once gpu screen recorder supports amd and intel properly
+ if(gpu_inf.vendor != GPU_VENDOR_NVIDIA) {
+ fprintf(stderr, "Error: gpu-screen-recorder does currently only support nvidia gpus\n");
+ return 2;
+ }
+
+ const char *screen_region = args["-s"].value();
const char *window_str = args["-w"].value();
+ if(screen_region && strcmp(window_str, "focused") != 0) {
+ fprintf(stderr, "Error: option -s is only available when using -w focused\n");
+ usage();
+ }
+
gsr_capture *capture = nullptr;
- if(contains_non_hex_number(window_str)) {
+ if(strcmp(window_str, "focused") == 0) {
+ if(!screen_region) {
+ fprintf(stderr, "Error: option -s is required when using -w focused\n");
+ usage();
+ }
+
+ vec2i region_size = { 0, 0 };
+ if(sscanf(screen_region, "%dx%d", &region_size.x, &region_size.y) != 2) {
+ fprintf(stderr, "Error: invalid value for option -s '%s', expected a value in format WxH\n", screen_region);
+ usage();
+ }
+
+ if(region_size.x <= 0 || region_size.y <= 0) {
+ fprintf(stderr, "Error: invalud value for option -s '%s', expected width and height to be greater than 0\n", screen_region);
+ usage();
+ }
+
+ switch(gpu_inf.vendor) {
+ case GPU_VENDOR_AMD: {
+ gsr_capture_xcomposite_drm_params xcomposite_params;
+ xcomposite_params.window = 0;
+ xcomposite_params.follow_focused = true;
+ xcomposite_params.region_size = region_size;
+ capture = gsr_capture_xcomposite_drm_create(&xcomposite_params);
+ if(!capture)
+ return 1;
+ break;
+ }
+ case GPU_VENDOR_INTEL: {
+ gsr_capture_xcomposite_drm_params xcomposite_params;
+ xcomposite_params.window = 0;
+ xcomposite_params.follow_focused = true;
+ xcomposite_params.region_size = region_size;
+ capture = gsr_capture_xcomposite_drm_create(&xcomposite_params);
+ if(!capture)
+ return 1;
+ break;
+ }
+ case GPU_VENDOR_NVIDIA: {
+ gsr_capture_xcomposite_cuda_params xcomposite_params;
+ xcomposite_params.window = 0;
+ xcomposite_params.follow_focused = true;
+ xcomposite_params.region_size = region_size;
+ capture = gsr_capture_xcomposite_cuda_create(&xcomposite_params);
+ if(!capture)
+ return 1;
+ break;
+ }
+ }
+ } else if(contains_non_hex_number(window_str)) {
if(gpu_inf.vendor != GPU_VENDOR_NVIDIA) {
- fprintf(stderr, "Error: recording a monitor is only supported on NVIDIA right now\n");
+ fprintf(stderr, "Error: recording a monitor is only supported on NVIDIA right now. Record \"focused\" instead for convenient fullscreen window recording\n");
return 2;
}
@@ -978,6 +1020,8 @@ int main(int argc, char **argv) {
case GPU_VENDOR_AMD: {
gsr_capture_xcomposite_drm_params xcomposite_params;
xcomposite_params.window = src_window_id;
+ xcomposite_params.follow_focused = false;
+ xcomposite_params.region_size = { 0, 0 };
capture = gsr_capture_xcomposite_drm_create(&xcomposite_params);
if(!capture)
return 1;
@@ -986,6 +1030,8 @@ int main(int argc, char **argv) {
case GPU_VENDOR_INTEL: {
gsr_capture_xcomposite_drm_params xcomposite_params;
xcomposite_params.window = src_window_id;
+ xcomposite_params.follow_focused = false;
+ xcomposite_params.region_size = { 0, 0 };
capture = gsr_capture_xcomposite_drm_create(&xcomposite_params);
if(!capture)
return 1;
@@ -994,6 +1040,8 @@ int main(int argc, char **argv) {
case GPU_VENDOR_NVIDIA: {
gsr_capture_xcomposite_cuda_params xcomposite_params;
xcomposite_params.window = src_window_id;
+ xcomposite_params.follow_focused = false;
+ xcomposite_params.region_size = { 0, 0 };
capture = gsr_capture_xcomposite_cuda_create(&xcomposite_params);
if(!capture)
return 1;
@@ -1052,7 +1100,7 @@ int main(int argc, char **argv) {
const double target_fps = 1.0 / (double)fps;
if(strcmp(codec_to_use, "auto") == 0) {
- const AVCodec *h265_codec = find_h265_encoder();
+ const AVCodec *h265_codec = find_h265_encoder(gpu_inf.vendor);
// h265 generally allows recording at a higher resolution than h264 on nvidia cards. On a gtx 1080 4k is the max resolution for h264 but for h265 it's 8k.
// Another important info is that when recording at a higher fps than.. 60? h265 has very bad performance. For example when recording at 144 fps the fps drops to 1
@@ -1081,10 +1129,10 @@ int main(int argc, char **argv) {
const AVCodec *video_codec_f = nullptr;
switch(video_codec) {
case VideoCodec::H264:
- video_codec_f = find_h264_encoder();
+ video_codec_f = find_h264_encoder(gpu_inf.vendor);
break;
case VideoCodec::H265:
- video_codec_f = find_h265_encoder();
+ video_codec_f = find_h265_encoder(gpu_inf.vendor);
break;
}
@@ -1104,7 +1152,7 @@ int main(int argc, char **argv) {
AVStream *video_stream = nullptr;
std::vector<AudioTrack> audio_tracks;
- AVCodecContext *video_codec_context = create_video_codec_context(AV_PIX_FMT_CUDA, quality, fps, video_codec_f, is_livestream);
+ AVCodecContext *video_codec_context = create_video_codec_context(gpu_inf.vendor == GPU_VENDOR_NVIDIA ? AV_PIX_FMT_CUDA : AV_PIX_FMT_VAAPI, quality, fps, video_codec_f, is_livestream);
if(replay_buffer_size_secs == -1)
video_stream = create_stream(av_format_context, video_codec_context);
diff --git a/src/window_texture.c b/src/window_texture.c
index cea77d7..d30ef61 100644
--- a/src/window_texture.c
+++ b/src/window_texture.c
@@ -1,6 +1,9 @@
#include "../include/window_texture.h"
#include <X11/extensions/Xcomposite.h>
-#include <stdio.h>
+
+#define EGL_TRUE 1
+#define EGL_IMAGE_PRESERVED_KHR 0x30D2
+#define EGL_NATIVE_PIXMAP_KHR 0x30B0
static int x11_supports_composite_named_window_pixmap(Display *display) {
int extension_major;
@@ -18,9 +21,6 @@ int window_texture_init(WindowTexture *window_texture, Display *display, Window
window_texture->window = window;
window_texture->pixmap = None;
window_texture->texture_id = 0;
- window_texture->target_texture_id = 0;
- window_texture->texture_width = 0;
- window_texture->texture_height = 0;
window_texture->redirected = 0;
window_texture->egl = egl;
@@ -38,11 +38,6 @@ static void window_texture_cleanup(WindowTexture *self, int delete_texture) {
self->texture_id = 0;
}
- if(delete_texture && self->target_texture_id) {
- self->egl->glDeleteTextures(1, &self->target_texture_id);
- self->target_texture_id = 0;
- }
-
if(self->pixmap) {
XFreePixmap(self->display, self->pixmap);
self->pixmap = None;
@@ -57,11 +52,6 @@ void window_texture_deinit(WindowTexture *self) {
window_texture_cleanup(self, 1);
}
-
-#define EGL_TRUE 1
-#define EGL_IMAGE_PRESERVED_KHR 0x30D2
-#define EGL_NATIVE_PIXMAP_KHR 0x30B0
-
int window_texture_on_resize(WindowTexture *self) {
window_texture_cleanup(self, 0);
@@ -84,7 +74,7 @@ int window_texture_on_resize(WindowTexture *self) {
if(self->texture_id == 0) {
self->egl->glGenTextures(1, &texture_id);
if(texture_id == 0) {
- result = 4;
+ result = 3;
goto cleanup;
}
self->egl->glBindTexture(GL_TEXTURE_2D, texture_id);
@@ -93,48 +83,39 @@ int window_texture_on_resize(WindowTexture *self) {
texture_id = self->texture_id;
}
+ self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
image = self->egl->eglCreateImage(self->egl->egl_display, NULL, EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer)pixmap, pixmap_attrs);
if(!image) {
- fprintf(stderr, "eglCreateImage failed\n");
- return -1;
+ result = 4;
+ goto cleanup;
}
- fprintf(stderr, "gl error: %d\n", self->egl->glGetError());
- fprintf(stderr, "image: %p\n", image);
self->egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
if(self->egl->glGetError() != 0) {
- fprintf(stderr, "glEGLImageTargetTexture2DOES failed\n");
+ result = 5;
+ goto cleanup;
}
- self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
- self->egl->glBindTexture(GL_TEXTURE_2D, 0);
-
self->pixmap = pixmap;
- if(texture_id != 0) {
- self->texture_id = texture_id;
+ self->texture_id = texture_id;
- self->egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &self->texture_width);
- self->egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &self->texture_height);
+ cleanup:
+ self->egl->glBindTexture(GL_TEXTURE_2D, 0);
- fprintf(stderr, "texture width: %d, height: %d\n", self->texture_width, self->texture_height);
+ if(image)
+ self->egl->eglDestroyImage(self->egl->egl_display, image);
- self->egl->glGenTextures(1, &self->target_texture_id);
- self->egl->glBindTexture(GL_TEXTURE_2D, self->target_texture_id);
- self->egl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, self->texture_width, self->texture_height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
- fprintf(stderr, "gl error: %d\n", self->egl->glGetError());
- self->egl->glBindTexture(GL_TEXTURE_2D, 0);
+ if(result != 0) {
+ if(texture_id != 0)
+ self->egl->glDeleteTextures(1, &texture_id);
+ if(pixmap)
+ XFreePixmap(self->display, pixmap);
}
- // TODO: destroyImage(image)
- return 0;
-
- cleanup:
- if(texture_id != 0) self->egl->glDeleteTextures(1, &texture_id);
- if(pixmap) XFreePixmap(self->display, pixmap);
return result;
}