#include "../../include/capture/xcomposite_drm.h" #include "../../include/egl.h" #include "../../include/window_texture.h" #include "../../include/time.h" #include #include #include #include #include #include #include #include //#include #include /* 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_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; } #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; } 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 // 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; cap_xcomp->egl.glClear(GL_COLOR_BUFFER_BIT); 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); } // 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) { 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; ipriv; vec2i source_size = cap_xcomp->texture_size; #if 1 /* 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, 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) { (void)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; }