diff options
Diffstat (limited to 'src/capture')
-rw-r--r-- | src/capture/nvfbc.c | 71 | ||||
-rw-r--r-- | src/capture/xcomposite_cuda.c (renamed from src/capture/xcomposite.c) | 213 | ||||
-rw-r--r-- | src/capture/xcomposite_drm.c | 855 |
3 files changed, 1002 insertions, 137 deletions
diff --git a/src/capture/nvfbc.c b/src/capture/nvfbc.c index a470879..17aedf8 100644 --- a/src/capture/nvfbc.c +++ b/src/capture/nvfbc.c @@ -22,6 +22,7 @@ typedef struct { bool fbc_handle_created; gsr_cuda cuda; + bool frame_initialized; } gsr_capture_nvfbc; #if defined(_WIN64) || defined(__LP64__) @@ -52,28 +53,45 @@ static uint32_t get_output_id_from_display_name(NVFBC_RANDR_OUTPUT_INFO *outputs } /* TODO: Test with optimus and open kernel modules */ -static bool driver_supports_direct_capture_cursor() { +static bool get_driver_version(int *major, int *minor) { + *major = 0; + *minor = 0; + FILE *f = fopen("/proc/driver/nvidia/version", "rb"); - if(!f) + if(!f) { + fprintf(stderr, "gsr warning: failed to get nvidia driver version (failed to read /proc/driver/nvidia/version)\n"); return false; + } char buffer[2048]; size_t bytes_read = fread(buffer, 1, sizeof(buffer) - 1, f); buffer[bytes_read] = '\0'; - bool supports_cursor = false; + bool success = false; const char *p = strstr(buffer, "Kernel Module"); if(p) { p += 13; int driver_major_version = 0, driver_minor_version = 0; if(sscanf(p, "%d.%d", &driver_major_version, &driver_minor_version) == 2) { - if(driver_major_version > 515 || (driver_major_version == 515 && driver_minor_version >= 57)) - supports_cursor = true; + *major = driver_major_version; + *minor = driver_minor_version; + success = true; } } + if(!success) + fprintf(stderr, "gsr warning: failed to get nvidia driver version\n"); + fclose(f); - return supports_cursor; + return success; +} + +static bool version_at_least(int major, int minor, int expected_major, int expected_minor) { + return major > expected_major || (major == expected_major && minor >= expected_minor); +} + +static bool version_less_than(int major, int minor, int expected_major, int expected_minor) { + return major < expected_major || (major == expected_major && minor < expected_minor); } static bool gsr_capture_nvfbc_load_library(gsr_capture *cap) { @@ -180,6 +198,30 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec const bool capture_region = (x > 0 || y > 0 || width > 0 || height > 0); + bool supports_direct_cursor = false; + bool direct_capture = cap_nvfbc->params.direct_capture; + int driver_major_version = 0; + int driver_minor_version = 0; + if(direct_capture && get_driver_version(&driver_major_version, &driver_minor_version)) { + fprintf(stderr, "Info: detected nvidia version: %d.%d\n", driver_major_version, driver_minor_version); + + if(version_at_least(driver_major_version, driver_minor_version, 515, 57) && version_less_than(driver_major_version, driver_minor_version, 520, 56)) { + direct_capture = false; + fprintf(stderr, "Warning: \"screen-direct\" has temporary been disabled as it causes stuttering with driver versions >= 515.57 and < 520.56. Please update your driver if possible. Capturing \"screen\" instead.\n"); + } + + // TODO: + // Cursor capture disabled because moving the cursor doesn't update capture rate to monitor hz and instead captures at 10-30 hz + /* + if(direct_capture) { + if(version_at_least(driver_major_version, driver_minor_version, 515, 57)) + supports_direct_cursor = true; + else + fprintf(stderr, "Info: capturing \"screen-direct\" but driver version appears to be less than 515.57. Disabling capture of cursor. Please update your driver if you want to capture your cursor or record \"screen\" instead.\n"); + } + */ + } + NVFBCSTATUS status; NVFBC_TRACKING_TYPE tracking_type; bool capture_session_created = false; @@ -245,11 +287,11 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec memset(&create_capture_params, 0, sizeof(create_capture_params)); create_capture_params.dwVersion = NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER; create_capture_params.eCaptureType = NVFBC_CAPTURE_SHARED_CUDA; - create_capture_params.bWithCursor = (!cap_nvfbc->params.direct_capture || driver_supports_direct_capture_cursor()) ? NVFBC_TRUE : NVFBC_FALSE; + create_capture_params.bWithCursor = (!direct_capture || supports_direct_cursor) ? NVFBC_TRUE : NVFBC_FALSE; if(capture_region) create_capture_params.captureBox = (NVFBC_BOX){ x, y, width, height }; create_capture_params.eTrackingType = tracking_type; - create_capture_params.dwSamplingRateMs = 1000u / (uint32_t)cap_nvfbc->params.fps; + create_capture_params.dwSamplingRateMs = 1000u / ((uint32_t)cap_nvfbc->params.fps + 1); create_capture_params.bAllowDirectCapture = cap_nvfbc->params.direct_capture ? NVFBC_TRUE : NVFBC_FALSE; create_capture_params.bPushModel = cap_nvfbc->params.direct_capture ? NVFBC_TRUE : NVFBC_FALSE; if(tracking_type == NVFBC_TRACKING_OUTPUT) @@ -324,6 +366,16 @@ static void gsr_capture_nvfbc_destroy_session(gsr_capture *cap) { cap_nvfbc->nv_fbc_handle = 0; } +static void gsr_capture_nvfbc_tick(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame **frame) { + gsr_capture_nvfbc *cap_nvfbc = cap->priv; + if(!cap_nvfbc->frame_initialized && video_codec_context->hw_frames_ctx) { + cap_nvfbc->frame_initialized = true; + (*frame)->hw_frames_ctx = video_codec_context->hw_frames_ctx; + (*frame)->buf[0] = av_buffer_pool_get(((AVHWFramesContext*)video_codec_context->hw_frames_ctx->data)->pool); + (*frame)->extended_data = (*frame)->data; + } +} + static int gsr_capture_nvfbc_capture(gsr_capture *cap, AVFrame *frame) { gsr_capture_nvfbc *cap_nvfbc = cap->priv; @@ -338,6 +390,7 @@ static int gsr_capture_nvfbc_capture(gsr_capture *cap, AVFrame *frame) { grab_params.dwFlags = NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT;/* | NVFBC_TOCUDA_GRAB_FLAGS_FORCE_REFRESH;*/ grab_params.pFrameGrabInfo = &frame_info; grab_params.pCUDADeviceBuffer = &cu_device_ptr; + grab_params.dwTimeoutMs = 0; NVFBCSTATUS status = cap_nvfbc->nv_fbc_function_list.nvFBCToCudaGrabFrame(cap_nvfbc->nv_fbc_handle, &grab_params); if(status != NVFBC_SUCCESS) { @@ -406,7 +459,7 @@ gsr_capture* gsr_capture_nvfbc_create(const gsr_capture_nvfbc_params *params) { *cap = (gsr_capture) { .start = gsr_capture_nvfbc_start, - .tick = NULL, + .tick = gsr_capture_nvfbc_tick, .should_stop = NULL, .capture = gsr_capture_nvfbc_capture, .destroy = gsr_capture_nvfbc_destroy, diff --git a/src/capture/xcomposite.c b/src/capture/xcomposite_cuda.c index 755ac92..d6c147b 100644 --- a/src/capture/xcomposite.c +++ b/src/capture/xcomposite_cuda.c @@ -1,5 +1,5 @@ -#include "../../include/capture/xcomposite.h" -#include "../../include/gl.h" +#include "../../include/capture/xcomposite_cuda.h" +#include "../../include/egl.h" #include "../../include/cuda.h" #include "../../include/window_texture.h" #include "../../include/time.h" @@ -9,10 +9,8 @@ #include <libavutil/frame.h> #include <libavcodec/avcodec.h> -/* TODO: Proper error checks and cleanups */ - typedef struct { - gsr_capture_xcomposite_params params; + gsr_capture_xcomposite_cuda_params params; Display *dpy; XEvent xev; bool should_stop; @@ -32,9 +30,9 @@ typedef struct { CUgraphicsResource cuda_graphics_resource; CUarray mapped_array; - gsr_gl gl; + gsr_egl egl; gsr_cuda cuda; -} gsr_capture_xcomposite; +} gsr_capture_xcomposite_cuda; static int max_int(int a, int b) { return a > b ? a : b; @@ -44,58 +42,9 @@ static int min_int(int a, int b) { return a < b ? a : b; } -static void gsr_capture_xcomposite_stop(gsr_capture *cap, AVCodecContext *video_codec_context); - -static Window get_compositor_window(Display *display) { - Window overlay_window = XCompositeGetOverlayWindow(display, DefaultRootWindow(display)); - XCompositeReleaseOverlayWindow(display, DefaultRootWindow(display)); - - Window root_window, parent_window; - Window *children = NULL; - unsigned int num_children = 0; - if(XQueryTree(display, overlay_window, &root_window, &parent_window, &children, &num_children) == 0) - return None; - - Window compositor_window = None; - if(num_children == 1) { - compositor_window = children[0]; - const int screen_width = XWidthOfScreen(DefaultScreenOfDisplay(display)); - const int screen_height = XHeightOfScreen(DefaultScreenOfDisplay(display)); - - XWindowAttributes attr; - if(!XGetWindowAttributes(display, compositor_window, &attr) || attr.width != screen_width || attr.height != screen_height) - compositor_window = None; - } - - if(children) - XFree(children); - - return compositor_window; -} - -/* TODO: check for glx swap control extension string (GLX_EXT_swap_control, etc) */ -static void set_vertical_sync_enabled(Display *display, Window window, gsr_gl *gl, bool enabled) { - int result = 0; - - if(gl->glXSwapIntervalEXT) { - gl->glXSwapIntervalEXT(display, window, enabled ? 1 : 0); - } else if(gl->glXSwapIntervalMESA) { - result = gl->glXSwapIntervalMESA(enabled ? 1 : 0); - } else if(gl->glXSwapIntervalSGI) { - result = gl->glXSwapIntervalSGI(enabled ? 1 : 0); - } else { - static int warned = 0; - if (!warned) { - warned = 1; - fprintf(stderr, "Warning: setting vertical sync not supported\n"); - } - } - - if(result != 0) - fprintf(stderr, "Warning: setting vertical sync failed\n"); -} +static void gsr_capture_xcomposite_cuda_stop(gsr_capture *cap, AVCodecContext *video_codec_context); -static bool cuda_register_opengl_texture(gsr_capture_xcomposite *cap_xcomp) { +static bool cuda_register_opengl_texture(gsr_capture_xcomposite_cuda *cap_xcomp) { CUresult res; CUcontext old_ctx; res = cap_xcomp->cuda.cuCtxPushCurrent_v2(cap_xcomp->cuda.cu_ctx); @@ -112,23 +61,22 @@ static bool cuda_register_opengl_texture(gsr_capture_xcomposite *cap_xcomp) { return false; } - /* Get texture */ 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); - /* Map texture to cuda array */ res = cap_xcomp->cuda.cuGraphicsSubResourceGetMappedArray(&cap_xcomp->mapped_array, cap_xcomp->cuda_graphics_resource, 0, 0); res = cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx); return true; } -static bool cuda_create_codec_context(gsr_capture_xcomposite *cap_xcomp, AVCodecContext *video_codec_context) { +static bool cuda_create_codec_context(gsr_capture_xcomposite_cuda *cap_xcomp, AVCodecContext *video_codec_context) { CUcontext old_ctx; cap_xcomp->cuda.cuCtxPushCurrent_v2(cap_xcomp->cuda.cu_ctx); AVBufferRef *device_ctx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_CUDA); if(!device_ctx) { fprintf(stderr, "Error: Failed to create hardware device context\n"); + cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx); return false; } @@ -173,7 +121,7 @@ static bool cuda_create_codec_context(gsr_capture_xcomposite *cap_xcomp, AVCodec return true; } -static unsigned int gl_create_texture(gsr_capture_xcomposite *cap_xcomp, int width, int height) { +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. @@ -182,25 +130,25 @@ static unsigned int gl_create_texture(gsr_capture_xcomposite *cap_xcomp, int wid // then needed every frame. // Ignoring failure for now.. TODO: Show proper error unsigned int texture_id = 0; - cap_xcomp->gl.glGenTextures(1, &texture_id); - cap_xcomp->gl.glBindTexture(GL_TEXTURE_2D, texture_id); - cap_xcomp->gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + cap_xcomp->egl.glGenTextures(1, &texture_id); + cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, texture_id); + cap_xcomp->egl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - cap_xcomp->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - cap_xcomp->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - cap_xcomp->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - cap_xcomp->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + 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->gl.glBindTexture(GL_TEXTURE_2D, 0); + cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, 0); return texture_id; } -static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_codec_context) { - gsr_capture_xcomposite *cap_xcomp = cap->priv; +static int gsr_capture_xcomposite_cuda_start(gsr_capture *cap, AVCodecContext *video_codec_context) { + gsr_capture_xcomposite_cuda *cap_xcomp = cap->priv; XWindowAttributes attr; if(!XGetWindowAttributes(cap_xcomp->dpy, cap_xcomp->params.window, &attr)) { - fprintf(stderr, "gsr error: gsr_capture_xcomposite_start failed: invalid window id: %lu\n", cap_xcomp->params.window); + fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_start failed: invalid window id: %lu\n", cap_xcomp->params.window); return -1; } @@ -211,32 +159,33 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_ XSelectInput(cap_xcomp->dpy, cap_xcomp->params.window, StructureNotifyMask | ExposureMask); - if(!gsr_gl_load(&cap_xcomp->gl, cap_xcomp->dpy)) { - fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: failed to load opengl\n"); + if(!gsr_egl_load(&cap_xcomp->egl, cap_xcomp->dpy)) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_start: failed to load opengl\n"); return -1; } - set_vertical_sync_enabled(cap_xcomp->dpy, cap_xcomp->gl.window, &cap_xcomp->gl, false); - if(window_texture_init(&cap_xcomp->window_texture, cap_xcomp->dpy, cap_xcomp->params.window, &cap_xcomp->gl) != 0) { - fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: failed get window texture for window %ld\n", cap_xcomp->params.window); - gsr_gl_unload(&cap_xcomp->gl); + 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); + gsr_egl_unload(&cap_xcomp->egl); return -1; } - cap_xcomp->gl.glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&cap_xcomp->window_texture)); + 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->gl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &cap_xcomp->texture_size.x); - cap_xcomp->gl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &cap_xcomp->texture_size.y); - cap_xcomp->gl.glBindTexture(GL_TEXTURE_2D, 0); + 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); 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); if(cap_xcomp->target_texture_id == 0) { - fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: failed to create opengl texture\n"); - gsr_capture_xcomposite_stop(cap, video_codec_context); + 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; } @@ -244,17 +193,17 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_ video_codec_context->height = cap_xcomp->texture_size.y; if(!gsr_cuda_load(&cap_xcomp->cuda)) { - gsr_capture_xcomposite_stop(cap, video_codec_context); + gsr_capture_xcomposite_cuda_stop(cap, video_codec_context); return -1; } if(!cuda_create_codec_context(cap_xcomp, video_codec_context)) { - gsr_capture_xcomposite_stop(cap, video_codec_context); + gsr_capture_xcomposite_cuda_stop(cap, video_codec_context); return -1; } if(!cuda_register_opengl_texture(cap_xcomp)) { - gsr_capture_xcomposite_stop(cap, video_codec_context); + gsr_capture_xcomposite_cuda_stop(cap, video_codec_context); return -1; } @@ -262,13 +211,13 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_ return 0; } -static void gsr_capture_xcomposite_stop(gsr_capture *cap, AVCodecContext *video_codec_context) { - gsr_capture_xcomposite *cap_xcomp = cap->priv; +static void gsr_capture_xcomposite_cuda_stop(gsr_capture *cap, AVCodecContext *video_codec_context) { + gsr_capture_xcomposite_cuda *cap_xcomp = cap->priv; window_texture_deinit(&cap_xcomp->window_texture); if(cap_xcomp->target_texture_id) { - cap_xcomp->gl.glDeleteTextures(1, &cap_xcomp->target_texture_id); + cap_xcomp->egl.glDeleteTextures(1, &cap_xcomp->target_texture_id); cap_xcomp->target_texture_id = 0; } @@ -280,35 +229,42 @@ static void gsr_capture_xcomposite_stop(gsr_capture *cap, AVCodecContext *video_ av_buffer_unref(&video_codec_context->hw_device_ctx); av_buffer_unref(&video_codec_context->hw_frames_ctx); - cap_xcomp->cuda.cuGraphicsUnmapResources(1, &cap_xcomp->cuda_graphics_resource, 0); - cap_xcomp->cuda.cuGraphicsUnregisterResource(cap_xcomp->cuda_graphics_resource); + if(cap_xcomp->cuda.cu_ctx) { + CUcontext old_ctx; + 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); + cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx); + } gsr_cuda_unload(&cap_xcomp->cuda); - gsr_gl_unload(&cap_xcomp->gl); + gsr_egl_unload(&cap_xcomp->egl); if(cap_xcomp->dpy) { + // TODO: Why is this crashing? XCloseDisplay(cap_xcomp->dpy); cap_xcomp->dpy = NULL; } } -static void gsr_capture_xcomposite_tick(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame **frame) { - gsr_capture_xcomposite *cap_xcomp = cap->priv; +static void gsr_capture_xcomposite_cuda_tick(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame **frame) { + gsr_capture_xcomposite_cuda *cap_xcomp = cap->priv; - cap_xcomp->gl.glClear(GL_COLOR_BUFFER_BIT); + cap_xcomp->egl.glClear(GL_COLOR_BUFFER_BIT); if(!cap_xcomp->created_hw_frame) { + cap_xcomp->created_hw_frame = true; CUcontext old_ctx; cap_xcomp->cuda.cuCtxPushCurrent_v2(cap_xcomp->cuda.cu_ctx); if(av_hwframe_get_buffer(video_codec_context->hw_frames_ctx, *frame, 0) < 0) { - fprintf(stderr, "gsr error: gsr_capture_xcomposite_tick: av_hwframe_get_buffer failed\n"); + 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; cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx); return; } - cap_xcomp->created_hw_frame = true; cap_xcomp->cuda.cuCtxPopCurrent_v2(&old_ctx); } @@ -343,25 +299,25 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap, AVCodecContext *video_ 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_tick: window_texture_on_resize failed\n"); + fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_tick: window_texture_on_resize failed\n"); cap_xcomp->should_stop = true; cap_xcomp->stop_is_error = true; return; } - cap_xcomp->gl.glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&cap_xcomp->window_texture)); + 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->gl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &cap_xcomp->texture_size.x); - cap_xcomp->gl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &cap_xcomp->texture_size.y); - cap_xcomp->gl.glBindTexture(GL_TEXTURE_2D, 0); + 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); 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->gl.glBindTexture(GL_TEXTURE_2D, cap_xcomp->target_texture_id); - cap_xcomp->gl.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->gl.glBindTexture(GL_TEXTURE_2D, 0); + 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); @@ -372,7 +328,7 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap, AVCodecContext *video_ if (res != CUDA_SUCCESS) { const char *err_str = "unknown"; cap_xcomp->cuda.cuGetErrorString(res, &err_str); - fprintf(stderr, "gsr error: gsr_capture_xcomposite_tick: cuGraphicsGLRegisterImage failed, error %s, texture id: %u\n", err_str, cap_xcomp->target_texture_id); + 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); @@ -386,7 +342,7 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap, AVCodecContext *video_ av_frame_free(frame); *frame = av_frame_alloc(); if(!frame) { - fprintf(stderr, "gsr error: gsr_capture_xcomposite_tick: failed to allocate frame\n"); + 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); @@ -395,9 +351,10 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap, AVCodecContext *video_ (*frame)->format = video_codec_context->pix_fmt; (*frame)->width = video_codec_context->width; (*frame)->height = video_codec_context->height; + (*frame)->color_range = AVCOL_RANGE_JPEG; if(av_hwframe_get_buffer(video_codec_context->hw_frames_ctx, *frame, 0) < 0) { - fprintf(stderr, "gsr error: gsr_capture_xcomposite_tick: av_hwframe_get_buffer failed\n"); + 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); @@ -411,8 +368,8 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap, AVCodecContext *video_ } } -static bool gsr_capture_xcomposite_should_stop(gsr_capture *cap, bool *err) { - gsr_capture_xcomposite *cap_xcomp = cap->priv; +static bool gsr_capture_xcomposite_cuda_should_stop(gsr_capture *cap, bool *err) { + gsr_capture_xcomposite_cuda *cap_xcomp = cap->priv; if(cap_xcomp->should_stop) { if(err) *err = cap_xcomp->stop_is_error; @@ -424,19 +381,19 @@ static bool gsr_capture_xcomposite_should_stop(gsr_capture *cap, bool *err) { return false; } -static int gsr_capture_xcomposite_capture(gsr_capture *cap, AVFrame *frame) { - gsr_capture_xcomposite *cap_xcomp = cap->priv; +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->gl.glCopyImageSubData( + 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->gl.glGetError(); + unsigned int err = cap_xcomp->egl.glGetError(); if(err != 0) { static bool error_shown = false; if(!error_shown) { @@ -444,7 +401,7 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, AVFrame *frame) { fprintf(stderr, "Error: glCopyImageSubData failed, gl error: %d\n", err); } } - cap_xcomp->gl.glXSwapBuffers(cap_xcomp->dpy, cap_xcomp->gl.window); + 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; @@ -468,8 +425,8 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, AVFrame *frame) { return 0; } -static void gsr_capture_xcomposite_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) { - gsr_capture_xcomposite_stop(cap, video_codec_context); +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) { free(cap->priv); cap->priv = NULL; @@ -477,9 +434,9 @@ static void gsr_capture_xcomposite_destroy(gsr_capture *cap, AVCodecContext *vid free(cap); } -gsr_capture* gsr_capture_xcomposite_create(const gsr_capture_xcomposite_params *params) { +gsr_capture* gsr_capture_xcomposite_cuda_create(const gsr_capture_xcomposite_cuda_params *params) { if(!params) { - fprintf(stderr, "gsr error: gsr_capture_xcomposite_create params is NULL\n"); + fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_create params is NULL\n"); return NULL; } @@ -487,7 +444,7 @@ gsr_capture* gsr_capture_xcomposite_create(const gsr_capture_xcomposite_params * if(!cap) return NULL; - gsr_capture_xcomposite *cap_xcomp = calloc(1, sizeof(gsr_capture_xcomposite)); + gsr_capture_xcomposite_cuda *cap_xcomp = calloc(1, sizeof(gsr_capture_xcomposite_cuda)); if(!cap_xcomp) { free(cap); return NULL; @@ -495,7 +452,7 @@ gsr_capture* gsr_capture_xcomposite_create(const gsr_capture_xcomposite_params * Display *display = XOpenDisplay(NULL); if(!display) { - fprintf(stderr, "gsr error: gsr_capture_xcomposite_create failed: XOpenDisplay failed\n"); + fprintf(stderr, "gsr error: gsr_capture_xcomposite_cuda_create failed: XOpenDisplay failed\n"); free(cap); free(cap_xcomp); return NULL; @@ -505,11 +462,11 @@ gsr_capture* gsr_capture_xcomposite_create(const gsr_capture_xcomposite_params * cap_xcomp->params = *params; *cap = (gsr_capture) { - .start = gsr_capture_xcomposite_start, - .tick = gsr_capture_xcomposite_tick, - .should_stop = gsr_capture_xcomposite_should_stop, - .capture = gsr_capture_xcomposite_capture, - .destroy = gsr_capture_xcomposite_destroy, + .start = gsr_capture_xcomposite_cuda_start, + .tick = gsr_capture_xcomposite_cuda_tick, + .should_stop = gsr_capture_xcomposite_cuda_should_stop, + .capture = gsr_capture_xcomposite_cuda_capture, + .destroy = gsr_capture_xcomposite_cuda_destroy, .priv = cap_xcomp }; diff --git a/src/capture/xcomposite_drm.c b/src/capture/xcomposite_drm.c new file mode 100644 index 0000000..65dec1e --- /dev/null +++ b/src/capture/xcomposite_drm.c @@ -0,0 +1,855 @@ +#include "../../include/capture/xcomposite_drm.h" +#include "../../include/egl.h" +#include "../../include/window_texture.h" +#include "../../include/time.h" +#include <stdlib.h> +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/extensions/Xcomposite.h> +#include <libavutil/hwcontext.h> +#include <libavutil/hwcontext_drm.h> +#include <libavutil/frame.h> +#include <libavcodec/avcodec.h> +//#include <drm_fourcc.h> +#include <assert.h> +/* TODO: Proper error checks and cleanups */ + +typedef struct { + gsr_capture_xcomposite_drm_params params; + Display *dpy; + XEvent xev; + bool created_hw_frame; + + vec2i window_pos; + vec2i window_size; + vec2i texture_size; + double window_resize_timer; + + WindowTexture window_texture; + + gsr_egl egl; + + int fourcc; + int num_planes; + uint64_t modifiers; + int dmabuf_fd; + int32_t stride; + int32_t offset; + + unsigned int target_texture_id; + + unsigned int FramebufferName; + unsigned int quad_VertexArrayID; + unsigned int quad_vertexbuffer; + unsigned int quadVAO; +} gsr_capture_xcomposite_drm; + +static int max_int(int a, int b) { + return a > b ? a : b; +} + +static int min_int(int a, int b) { + return a < b ? a : b; +} + +static bool drm_create_codec_context(gsr_capture_xcomposite_drm *cap_xcomp, AVCodecContext *video_codec_context) { + // TODO: "/dev/dri/card0" + AVBufferRef *device_ctx; + if(av_hwdevice_ctx_create(&device_ctx, AV_HWDEVICE_TYPE_VAAPI, "/dev/dri/card0", NULL, 0) < 0) { + fprintf(stderr, "Error: Failed to create hardware device context\n"); + return false; + } + + AVBufferRef *frame_context = av_hwframe_ctx_alloc(device_ctx); + if(!frame_context) { + fprintf(stderr, "Error: Failed to create hwframe context\n"); + av_buffer_unref(&device_ctx); + return false; + } + + AVHWFramesContext *hw_frame_context = + (AVHWFramesContext *)frame_context->data; + hw_frame_context->width = video_codec_context->width; + hw_frame_context->height = video_codec_context->height; + hw_frame_context->sw_format = AV_PIX_FMT_YUV420P;//AV_PIX_FMT_0RGB32;//AV_PIX_FMT_YUV420P;//AV_PIX_FMT_0RGB32;//AV_PIX_FMT_NV12; + hw_frame_context->format = video_codec_context->pix_fmt; + hw_frame_context->device_ref = device_ctx; + hw_frame_context->device_ctx = (AVHWDeviceContext*)device_ctx->data; + + if (av_hwframe_ctx_init(frame_context) < 0) { + fprintf(stderr, "Error: Failed to initialize hardware frame context " + "(note: ffmpeg version needs to be > 4.0)\n"); + av_buffer_unref(&device_ctx); + av_buffer_unref(&frame_context); + return false; + } + + video_codec_context->hw_device_ctx = device_ctx; // TODO: av_buffer_ref? and in more places + video_codec_context->hw_frames_ctx = frame_context; + return true; +} + +#define EGL_SURFACE_TYPE 0x3033 +#define EGL_WINDOW_BIT 0x0004 +#define EGL_PIXMAP_BIT 0x0002 +#define EGL_BIND_TO_TEXTURE_RGB 0x3039 +#define EGL_TRUE 1 +#define EGL_RED_SIZE 0x3024 +#define EGL_GREEN_SIZE 0x3023 +#define EGL_BLUE_SIZE 0x3022 +#define EGL_ALPHA_SIZE 0x3021 +#define EGL_TEXTURE_FORMAT 0x3080 +#define EGL_TEXTURE_RGB 0x305D +#define EGL_TEXTURE_TARGET 0x3081 +#define EGL_TEXTURE_2D 0x305F +#define EGL_GL_TEXTURE_2D 0x30B1 + +#define GL_RGBA 0x1908 + +static unsigned int gl_create_texture(gsr_capture_xcomposite_drm *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); + cap_xcomp->egl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + 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.glBindTexture(GL_TEXTURE_2D, 0); + return texture_id; +} + +#define GL_COMPILE_STATUS 0x8B81 +#define GL_INFO_LOG_LENGTH 0x8B84 + +unsigned int esLoadShader ( gsr_capture_xcomposite_drm *cap_xcomp, unsigned int type, const char *shaderSrc ) { + unsigned int shader; + int compiled; + + // Create the shader object + shader = cap_xcomp->egl.glCreateShader ( type ); + + if ( shader == 0 ) + return 0; + + // Load the shader source + cap_xcomp->egl.glShaderSource ( shader, 1, &shaderSrc, NULL ); + + // Compile the shader + cap_xcomp->egl.glCompileShader ( shader ); + + // Check the compile status + cap_xcomp->egl.glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled ); + + if ( !compiled ) + { + int infoLen = 0; + + cap_xcomp->egl.glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen ); + + if ( infoLen > 1 ) + { + char* infoLog = malloc (sizeof(char) * infoLen ); + + cap_xcomp->egl.glGetShaderInfoLog ( shader, infoLen, NULL, infoLog ); + fprintf (stderr, "Error compiling shader:\n%s\n", infoLog ); + + free ( infoLog ); + } + + cap_xcomp->egl.glDeleteShader ( shader ); + return 0; + } + + return shader; + +} + +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 + + +// +/// +/// \brief Load a vertex and fragment shader, create a program object, link program. +// Errors output to log. +/// \param vertShaderSrc Vertex shader source code +/// \param fragShaderSrc Fragment shader source code +/// \return A new program object linked with the vertex/fragment shader pair, 0 on failure +// +unsigned int esLoadProgram ( gsr_capture_xcomposite_drm *cap_xcomp, const char *vertShaderSrc, const char *fragShaderSrc ) +{ + unsigned int vertexShader; + unsigned int fragmentShader; + unsigned int programObject; + int linked; + + // Load the vertex/fragment shaders + vertexShader = esLoadShader ( cap_xcomp, GL_VERTEX_SHADER, vertShaderSrc ); + if ( vertexShader == 0 ) + return 0; + + fragmentShader = esLoadShader ( cap_xcomp, GL_FRAGMENT_SHADER, fragShaderSrc ); + if ( fragmentShader == 0 ) + { + cap_xcomp->egl.glDeleteShader( vertexShader ); + return 0; + } + + // Create the program object + programObject = cap_xcomp->egl.glCreateProgram ( ); + + if ( programObject == 0 ) + return 0; + + cap_xcomp->egl.glAttachShader ( programObject, vertexShader ); + cap_xcomp->egl.glAttachShader ( programObject, fragmentShader ); + + // Link the program + cap_xcomp->egl.glLinkProgram ( programObject ); + + // Check the link status + cap_xcomp->egl.glGetProgramiv ( programObject, GL_LINK_STATUS, &linked ); + + if ( !linked ) + { + int infoLen = 0; + + cap_xcomp->egl.glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen ); + + if ( infoLen > 1 ) + { + char* infoLog = malloc (sizeof(char) * infoLen ); + + cap_xcomp->egl.glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog ); + fprintf (stderr, "Error linking program:\n%s\n", infoLog ); + + free ( infoLog ); + } + + cap_xcomp->egl.glDeleteProgram ( programObject ); + return 0; + } + + // Free up no longer needed shader resources + cap_xcomp->egl.glDeleteShader ( vertexShader ); + cap_xcomp->egl.glDeleteShader ( fragmentShader ); + + return programObject; +} + +static unsigned int shader_program = 0; +static unsigned int texID = 0; + +static void LoadShaders(gsr_capture_xcomposite_drm *cap_xcomp) { + char vShaderStr[] = + "#version 300 es \n" + "in vec2 pos; \n" + "in vec2 texcoords; \n" + "out vec2 texcoords_out; \n" + "void main() \n" + "{ \n" + " texcoords_out = texcoords; \n" + " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0); \n" + "} \n"; + +#if 0 + char fShaderStr[] = + "#version 300 es \n" + "precision mediump float; \n" + "in vec2 texcoords_out; \n" + "uniform sampler2D tex; \n" + "out vec4 FragColor; \n" + + + "float imageWidth = 1920.0;\n" + "float imageHeight = 1080.0;\n" + + "float getYPixel(vec2 position) {\n" + " position.y = (position.y * 2.0 / 3.0) + (1.0 / 3.0);\n" + " return texture2D(tex, position).x;\n" + "}\n" +"\n" + "vec2 mapCommon(vec2 position, float planarOffset) {\n" + " planarOffset += (imageWidth * floor(position.y / 2.0)) / 2.0 +\n" + " floor((imageWidth - 1.0 - position.x) / 2.0);\n" + " float x = floor(imageWidth - 1.0 - floor(mod(planarOffset, imageWidth)));\n" + " float y = floor(floor(planarOffset / imageWidth));\n" + " return vec2((x + 0.5) / imageWidth, (y + 0.5) / (1.5 * imageHeight));\n" + "}\n" +"\n" + "vec2 mapU(vec2 position) {\n" + " float planarOffset = (imageWidth * imageHeight) / 4.0;\n" + " return mapCommon(position, planarOffset);\n" + "}\n" +"\n" + "vec2 mapV(vec2 position) {\n" + " return mapCommon(position, 0.0);\n" + "}\n" + + "void main() \n" + "{ \n" + + "vec2 pixelPosition = vec2(floor(imageWidth * texcoords_out.x),\n" + " floor(imageHeight * texcoords_out.y));\n" + "pixelPosition -= vec2(0.5, 0.5);\n" +"\n" + "float yChannel = getYPixel(texcoords_out);\n" + "float uChannel = texture2D(tex, mapU(pixelPosition)).x;\n" + "float vChannel = texture2D(tex, mapV(pixelPosition)).x;\n" + "vec4 channels = vec4(yChannel, uChannel, vChannel, 1.0);\n" + "mat4 conversion = mat4(1.0, 0.0, 1.402, -0.701,\n" + " 1.0, -0.344, -0.714, 0.529,\n" + " 1.0, 1.772, 0.0, -0.886,\n" + " 0, 0, 0, 0);\n" + "vec3 rgb = (channels * conversion).xyz;\n" + + " FragColor = vec4(rgb, 1.0); \n" + "} \n"; +#elif 1 + char fShaderStr[] = + "#version 300 es \n" + "precision mediump float; \n" + "in vec2 texcoords_out; \n" + "uniform sampler2D tex; \n" + "out vec4 FragColor; \n" + "void main() \n" + "{ \n" + " vec3 rgb = texture(tex, texcoords_out).rgb; \n" + " FragColor = vec4(rgb, 1.0); \n" + "} \n"; +#else + char fShaderStr[] = + "#version 300 es \n" + "precision mediump float; \n" + "in vec2 texcoords_out; \n" + "uniform sampler2D tex; \n" + "out vec4 FragColor; \n" + + "vec3 rgb2yuv(vec3 rgb){\n" + " float y = 0.299*rgb.r + 0.587*rgb.g + 0.114*rgb.b;\n" + " return vec3(y, 0.493*(rgb.b-y), 0.877*(rgb.r-y));\n" + "}\n" + + "vec3 yuv2rgb(vec3 yuv){\n" + " float y = yuv.x;\n" + " float u = yuv.y;\n" + " float v = yuv.z;\n" + " \n" + " return vec3(\n" + " y + 1.0/0.877*v,\n" + " y - 0.39393*u - 0.58081*v,\n" + " y + 1.0/0.493*u\n" + " );\n" + "}\n" + + "void main() \n" + "{ \n" + " float s = 0.5;\n" + " vec3 lum = texture(tex, texcoords_out).rgb;\n" + " vec3 chr = texture(tex, floor(texcoords_out*s-.5)/s).rgb;\n" + " vec3 rgb = vec3(rgb2yuv(lum).x, rgb2yuv(chr).yz);\n" + " FragColor = vec4(rgb, 1.0); \n" + "} \n"; +#endif + + shader_program = esLoadProgram(cap_xcomp, vShaderStr, fShaderStr); + if (shader_program == 0) { + fprintf(stderr, "failed to create shader!\n"); + return; + } + + cap_xcomp->egl.glBindAttribLocation(shader_program, 0, "pos"); + cap_xcomp->egl.glBindAttribLocation(shader_program, 1, "texcoords"); + return; +} + +#define GL_FLOAT 0x1406 +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_TRIANGLES 0x0004 +#define DRM_FORMAT_MOD_INVALID 72057594037927935 + +static int gsr_capture_xcomposite_drm_start(gsr_capture *cap, AVCodecContext *video_codec_context) { + gsr_capture_xcomposite_drm *cap_xcomp = cap->priv; + + XWindowAttributes attr; + if(!XGetWindowAttributes(cap_xcomp->dpy, cap_xcomp->params.window, &attr)) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_start failed: invalid window id: %lu\n", cap_xcomp->params.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); + + // TODO: Get select and add these on top of it and then restore at the end. Also do the same in other xcomposite + XSelectInput(cap_xcomp->dpy, cap_xcomp->params.window, StructureNotifyMask | ExposureMask); + + if(!gsr_egl_load(&cap_xcomp->egl, cap_xcomp->dpy)) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: failed to load opengl\n"); + return -1; + } + + /* Disable vsync */ + cap_xcomp->egl.eglSwapInterval(cap_xcomp->egl.egl_display, 0); +#if 0 + // TODO: Fallback to composite window + if(window_texture_init(&cap_xcomp->window_texture, cap_xcomp->dpy, cap_xcomp->params.window, &cap_xcomp->gl) != 0) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: failed get window texture for window %ld\n", cap_xcomp->params.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.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); + + 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); + if(cap_xcomp->target_texture_id == 0) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: failed to create opengl texture\n"); + gsr_capture_xcomposite_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; + + cap_xcomp->window_resize_timer = clock_get_monotonic_seconds(); + return 0; +#else + // 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_drm_start: failed get window texture for window %ld\n", cap_xcomp->params.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.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); + + #if 1 + cap_xcomp->target_texture_id = gl_create_texture(cap_xcomp, cap_xcomp->texture_size.x, cap_xcomp->texture_size.y); + if(cap_xcomp->target_texture_id == 0) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_drm_start: failed to create opengl texture\n"); + return -1; + } + #else + // TODO: + cap_xcomp->target_texture_id = window_texture_get_opengl_texture_id(&cap_xcomp->window_texture); + #endif + + 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); + + video_codec_context->width = cap_xcomp->texture_size.x; + video_codec_context->height = cap_xcomp->texture_size.y; + + { + EGLImage img = cap_xcomp->egl.eglCreateImage(cap_xcomp->egl.egl_display, cap_xcomp->egl.egl_context, EGL_GL_TEXTURE_2D, (EGLClientBuffer)(uint64_t)cap_xcomp->target_texture_id, NULL); + if(!img) { + fprintf(stderr, "eglCreateImage failed\n"); + return -1; + } + + if(!cap_xcomp->egl.eglExportDMABUFImageQueryMESA(cap_xcomp->egl.egl_display, img, &cap_xcomp->fourcc, &cap_xcomp->num_planes, &cap_xcomp->modifiers) || cap_xcomp->modifiers == DRM_FORMAT_MOD_INVALID) { + fprintf(stderr, "eglExportDMABUFImageQueryMESA failed\n"); + return -1; + } + + if(cap_xcomp->num_planes != 1) { + // TODO: FAIL! + fprintf(stderr, "Blablalba\n"); + return -1; + } + + if(!cap_xcomp->egl.eglExportDMABUFImageMESA(cap_xcomp->egl.egl_display, img, &cap_xcomp->dmabuf_fd, &cap_xcomp->stride, &cap_xcomp->offset)) { + fprintf(stderr, "eglExportDMABUFImageMESA failed\n"); + return -1; + } + + fprintf(stderr, "texture: %u, dmabuf: %d, stride: %d, offset: %d\n", cap_xcomp->target_texture_id, cap_xcomp->dmabuf_fd, cap_xcomp->stride, cap_xcomp->offset); + fprintf(stderr, "fourcc: %d, num planes: %d, modifiers: %zu\n", cap_xcomp->fourcc, cap_xcomp->num_planes, cap_xcomp->modifiers); + } + + cap_xcomp->egl.glGenFramebuffers(1, &cap_xcomp->FramebufferName); + cap_xcomp->egl.glBindFramebuffer(GL_FRAMEBUFFER, cap_xcomp->FramebufferName); + + cap_xcomp->egl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cap_xcomp->target_texture_id, 0); + + // Set the list of draw buffers. + unsigned int DrawBuffers[1] = {GL_COLOR_ATTACHMENT0}; + cap_xcomp->egl.glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers + + if(cap_xcomp->egl.glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + fprintf(stderr, "Failed to setup framebuffer\n"); + return -1; + } + + cap_xcomp->egl.glBindFramebuffer(GL_FRAMEBUFFER, 0); + + //cap_xcomp->egl.glGenVertexArrays(1, &cap_xcomp->quad_VertexArrayID); + //cap_xcomp->egl.glBindVertexArray(cap_xcomp->quad_VertexArrayID); + + static const float g_quad_vertex_buffer_data[] = { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, + }; + + //cap_xcomp->egl.glGenBuffers(1, &cap_xcomp->quad_vertexbuffer); + //cap_xcomp->egl.glBindBuffer(GL_ARRAY_BUFFER, cap_xcomp->quad_vertexbuffer); + //cap_xcomp->egl.glBufferData(GL_ARRAY_BUFFER, sizeof(g_quad_vertex_buffer_data), g_quad_vertex_buffer_data, GL_STATIC_DRAW); + + // Create and compile our GLSL program from the shaders + LoadShaders(cap_xcomp); + texID = cap_xcomp->egl.glGetUniformLocation(shader_program, "tex"); + fprintf(stderr, "uniform id: %u\n", texID); + + float vVertices[] = { + -1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + + -1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f + }; + + unsigned int quadVBO; + cap_xcomp->egl.glGenVertexArrays(1, &cap_xcomp->quadVAO); + cap_xcomp->egl.glGenBuffers(1, &quadVBO); + cap_xcomp->egl.glBindVertexArray(cap_xcomp->quadVAO); + cap_xcomp->egl.glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + cap_xcomp->egl.glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices), &vVertices, GL_STATIC_DRAW); + + cap_xcomp->egl.glEnableVertexAttribArray(0); + cap_xcomp->egl.glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + + cap_xcomp->egl.glEnableVertexAttribArray(1); + cap_xcomp->egl.glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + + cap_xcomp->egl.glBindVertexArray(0); + + //cap_xcomp->egl.glUniform1i(texID, window_texture_get_opengl_texture_id(&cap_xcomp->window_texture)); + + //cap_xcomp->egl.glViewport(0, 0, 1920, 1080); + + //cap_xcomp->egl.glBindBuffer(GL_ARRAY_BUFFER, 0); + //cap_xcomp->egl.glBindVertexArray(0); + + if(!drm_create_codec_context(cap_xcomp, video_codec_context)) { + fprintf(stderr, "failed to create hw codec context\n"); + gsr_egl_unload(&cap_xcomp->egl); + return -1; + } + + fprintf(stderr, "sneed: %u\n", cap_xcomp->FramebufferName); + return 0; +#endif +} + +// TODO: +static void free_desc(void *opaque, uint8_t *data) { + AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)data; + int i; + + //for (i = 0; i < desc->nb_objects; i++) + // close(desc->objects[i].fd); + + av_free(desc); +} + + +static void gsr_capture_xcomposite_drm_tick(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame **frame) { + gsr_capture_xcomposite_drm *cap_xcomp = cap->priv; + + if(!cap_xcomp->created_hw_frame) { + cap_xcomp->created_hw_frame = true; + + /*if(av_hwframe_get_buffer(video_codec_context->hw_frames_ctx, *frame, 0) < 0) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_drm_tick: av_hwframe_get_buffer failed\n"); + return; + }*/ + + AVDRMFrameDescriptor *desc = av_malloc(sizeof(AVDRMFrameDescriptor)); + if(!desc) { + fprintf(stderr, "poop\n"); + return; + } + + fprintf(stderr, "tick fd: %d\n", cap_xcomp->dmabuf_fd); + + cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, cap_xcomp->target_texture_id); + int xx = 0; + int yy = 0; + cap_xcomp->egl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &xx); + cap_xcomp->egl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &yy); + cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, 0); + + *desc = (AVDRMFrameDescriptor) { + .nb_objects = 1, + .objects[0] = { + .fd = cap_xcomp->dmabuf_fd, + .size = yy * cap_xcomp->stride, + .format_modifier = cap_xcomp->modifiers, + }, + .nb_layers = 1, + .layers[0] = { + .format = cap_xcomp->fourcc, // DRM_FORMAT_NV12 + .nb_planes = 1, //cap_xcomp->num_planes, // TODO: Ensure this is 1, otherwise ffmpeg cant handle it in av_hwframe_map + .planes[0] = { + .object_index = 0, + .offset = cap_xcomp->offset, + .pitch = cap_xcomp->stride, + }, + }, + }; + + #if 0 + AVBufferRef *device_ctx; + if(av_hwdevice_ctx_create(&device_ctx, AV_HWDEVICE_TYPE_DRM, "/dev/dri/card0", NULL, 0) < 0) { + fprintf(stderr, "Error: Failed to create hardware device context\n"); + return; + } + + AVBufferRef *frame_context = av_hwframe_ctx_alloc(device_ctx); + if(!frame_context) { + fprintf(stderr, "Error: Failed to create hwframe context\n"); + av_buffer_unref(&device_ctx); + return; + } + + AVHWFramesContext *hw_frame_context = + (AVHWFramesContext *)frame_context->data; + hw_frame_context->width = video_codec_context->width; + hw_frame_context->height = video_codec_context->height; + hw_frame_context->sw_format = AV_PIX_FMT_0RGB32; + hw_frame_context->format = AV_PIX_FMT_DRM_PRIME; + hw_frame_context->device_ref = device_ctx; + hw_frame_context->device_ctx = (AVHWDeviceContext*)device_ctx->data; + + if (av_hwframe_ctx_init(frame_context) < 0) { + fprintf(stderr, "Error: Failed to initialize hardware frame context " + "(note: ffmpeg version needs to be > 4.0)\n"); + av_buffer_unref(&device_ctx); + av_buffer_unref(&frame_context); + return; + } + #endif + + av_frame_free(frame); + *frame = av_frame_alloc(); + if(!frame) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_tick: failed to allocate frame\n"); + return; + } + (*frame)->format = video_codec_context->pix_fmt; + (*frame)->width = video_codec_context->width; + (*frame)->height = video_codec_context->height; + (*frame)->color_range = AVCOL_RANGE_JPEG; + + int res = av_hwframe_get_buffer(video_codec_context->hw_frames_ctx, *frame, 0); + if(res < 0) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_tick: av_hwframe_get_buffer failed 1: %d\n", res); + return; + } + + AVFrame *src_frame = av_frame_alloc(); + assert(src_frame); + src_frame->format = AV_PIX_FMT_DRM_PRIME; + src_frame->width = video_codec_context->width; + src_frame->height = video_codec_context->height; + src_frame->color_range = AVCOL_RANGE_JPEG; + + src_frame->buf[0] = av_buffer_create((uint8_t*)desc, sizeof(*desc), + &free_desc, video_codec_context, 0); + if (!src_frame->buf[0]) { + fprintf(stderr, "failed to create buffer!\n"); + return; + } + + src_frame->data[0] = (uint8_t*)desc; + src_frame->extended_data = src_frame->data; + src_frame->format = AV_PIX_FMT_DRM_PRIME; + + res = av_hwframe_map(*frame, src_frame, AV_HWFRAME_MAP_DIRECT); + if(res < 0) { + fprintf(stderr, "av_hwframe_map failed: %d\n", res); + } + } + + cap_xcomp->egl.glClear(GL_COLOR_BUFFER_BIT); +} + +static bool gsr_capture_xcomposite_drm_should_stop(gsr_capture *cap, bool *err) { + return false; +} + +#define GL_FLOAT 0x1406 +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_TRIANGLES 0x0004 + +void FBO_2_PPM_file(gsr_capture_xcomposite_drm *cap_xcomp, int output_width, int output_height) +{ + FILE *output_image; + + /// READ THE PIXELS VALUES from FBO AND SAVE TO A .PPM FILE + int i, j, k; + unsigned char *pixels = (unsigned char*)malloc(output_width*output_height*3); + + unsigned int err = cap_xcomp->egl.glGetError(); + fprintf(stderr, "opengl err 1: %u\n", err); + + /// READ THE CONTENT FROM THE FBO + cap_xcomp->egl.glReadBuffer(GL_COLOR_ATTACHMENT0); + + err = cap_xcomp->egl.glGetError(); + fprintf(stderr, "opengl err 2: %u\n", err); + + cap_xcomp->egl.glReadPixels(0, 0, output_width, output_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + err = cap_xcomp->egl.glGetError(); + fprintf(stderr, "opengl err 3: %u\n", err); + + output_image = fopen("output.ppm", "wb"); + fprintf(output_image,"P3\n"); + fprintf(output_image,"# Created by Ricao\n"); + fprintf(output_image,"%d %d\n",output_width,output_height); + fprintf(output_image,"255\n"); + + k = 0; + for(i=0; i<output_width; i++) + { + for(j=0; j<output_height; j++) + { + fprintf(output_image,"%u %u %u ",(unsigned int)pixels[k],(unsigned int)pixels[k+1], + (unsigned int)pixels[k+2]); + k = k+4; + } + fprintf(output_image,"\n"); + } + free(pixels); + fclose(output_image); +} + +static int gsr_capture_xcomposite_drm_capture(gsr_capture *cap, AVFrame *frame) { + gsr_capture_xcomposite_drm *cap_xcomp = cap->priv; + vec2i source_size = cap_xcomp->texture_size; + + #if 1 + // 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, 0, 0, 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); + } + } + #elif 0 + cap_xcomp->egl.glBindFramebuffer(GL_FRAMEBUFFER, cap_xcomp->FramebufferName); + cap_xcomp->egl.glViewport(0, 0, 1920, 1080); + //cap_xcomp->egl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + cap_xcomp->egl.glClear(GL_COLOR_BUFFER_BIT); + + cap_xcomp->egl.glUseProgram(shader_program); + cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&cap_xcomp->window_texture)); + cap_xcomp->egl.glBindVertexArray(cap_xcomp->quadVAO); + cap_xcomp->egl.glDrawArrays(GL_TRIANGLES, 0, 6); + cap_xcomp->egl.glBindTexture(GL_TEXTURE_2D, 0); + + static int counter = 0; + ++counter; + static bool image_saved = false; + if(!image_saved && counter == 5) { + image_saved = true; + FBO_2_PPM_file(cap_xcomp, 1920, 1080); + fprintf(stderr, "saved image!\n"); + } + + cap_xcomp->egl.glBindVertexArray(0); + cap_xcomp->egl.glUseProgram(0); + cap_xcomp->egl.glBindFramebuffer(GL_FRAMEBUFFER, 0); + #endif + cap_xcomp->egl.eglSwapBuffers(cap_xcomp->egl.egl_display, cap_xcomp->egl.egl_surface); + + return 0; +} + +static void gsr_capture_xcomposite_drm_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) { + if(cap->priv) { + free(cap->priv); + cap->priv = NULL; + } + free(cap); +} + +gsr_capture* gsr_capture_xcomposite_drm_create(const gsr_capture_xcomposite_drm_params *params) { + if(!params) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_drm_create params is NULL\n"); + return NULL; + } + + gsr_capture *cap = calloc(1, sizeof(gsr_capture)); + if(!cap) + return NULL; + + gsr_capture_xcomposite_drm *cap_xcomp = calloc(1, sizeof(gsr_capture_xcomposite_drm)); + if(!cap_xcomp) { + free(cap); + return NULL; + } + + Display *display = XOpenDisplay(NULL); + if(!display) { + fprintf(stderr, "gsr error: gsr_capture_xcomposite_drm_create failed: XOpenDisplay failed\n"); + free(cap); + free(cap_xcomp); + return NULL; + } + + cap_xcomp->dpy = display; + cap_xcomp->params = *params; + + *cap = (gsr_capture) { + .start = gsr_capture_xcomposite_drm_start, + .tick = gsr_capture_xcomposite_drm_tick, + .should_stop = gsr_capture_xcomposite_drm_should_stop, + .capture = gsr_capture_xcomposite_drm_capture, + .destroy = gsr_capture_xcomposite_drm_destroy, + .priv = cap_xcomp + }; + + return cap; +} |