Fix v4l2 mjpeg on nvidia, yuyv on nvidia not done

This commit is contained in:
dec05eba
2025-12-24 02:00:20 +01:00
parent ae0eb0252c
commit 0511f04d1f
8 changed files with 161 additions and 49 deletions

View File

@@ -250,7 +250,7 @@ static bool gsr_capture_v4l2_map_buffer(gsr_capture_v4l2 *self, const struct v4l
self->dma_image[i] = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, (intptr_t[]) {
EGL_WIDTH, fmt->fmt.pix.width,
EGL_HEIGHT, fmt->fmt.pix.height,
EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_YUYV,
EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_YUYV, // TODO: Use DRM_FORMAT_RG88 on nvidia and custom shader. Test on intel as well, or use fallback method on every system.
EGL_DMA_BUF_PLANE0_FD_EXT, self->dmabuf_fd[i],
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
EGL_DMA_BUF_PLANE0_PITCH_EXT, fmt->fmt.pix.bytesperline,
@@ -490,7 +490,7 @@ static void gsr_capture_v4l2_decode_jpeg_to_texture(gsr_capture_v4l2 *self, cons
self->params.egl->glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self->pbos[next_pbo_index]);
self->params.egl->glBufferData(GL_PIXEL_UNPACK_BUFFER, self->capture_size.x * self->capture_size.y * 4, 0, GL_DYNAMIC_DRAW);
void *mapped_buffer = self->params.egl->glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
void *mapped_buffer = self->params.egl->glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, self->capture_size.x * self->capture_size.y * 4, GL_MAP_WRITE_BIT);
if(mapped_buffer) {
if(self->tjDecompress2(self->jpeg_decompressor, self->dmabuf_map[buf->index], buf->bytesused, mapped_buffer, jpeg_width, 0, jpeg_height, TJPF_RGBA, TJFLAG_FASTDCT) != 0)
fprintf(stderr, "gsr error: gsr_capture_v4l2_capture: failed to decompress camera jpeg data, error: %s\n", self->tjGetErrorStr2(self->jpeg_decompressor));
@@ -536,6 +536,8 @@ static int gsr_capture_v4l2_capture(gsr_capture *cap, gsr_capture_metadata *capt
const vec2i output_size = scale_keep_aspect_ratio(self->capture_size, capture_metadata->recording_size);
const vec2i target_pos = gsr_capture_get_target_position(output_size, capture_metadata);
self->params.egl->glFlush();
//if(self->got_first_frame) {
gsr_color_conversion_draw(color_conversion, self->texture_id,
target_pos, output_size,

View File

@@ -4,12 +4,14 @@
#include <string.h>
#include <assert.h>
#define GRAPHICS_SHADER_INDEX_Y 0
#define GRAPHICS_SHADER_INDEX_UV 1
#define GRAPHICS_SHADER_INDEX_Y_EXTERNAL 2
#define GRAPHICS_SHADER_INDEX_UV_EXTERNAL 3
#define GRAPHICS_SHADER_INDEX_RGB 4
#define GRAPHICS_SHADER_INDEX_RGB_EXTERNAL 5
#define GRAPHICS_SHADER_INDEX_Y 0
#define GRAPHICS_SHADER_INDEX_UV 1
#define GRAPHICS_SHADER_INDEX_Y_EXTERNAL 2
#define GRAPHICS_SHADER_INDEX_UV_EXTERNAL 3
#define GRAPHICS_SHADER_INDEX_RGB 4
#define GRAPHICS_SHADER_INDEX_RGB_EXTERNAL 5
#define GRAPHICS_SHADER_INDEX_YUYV_TO_RGB 6
#define GRAPHICS_SHADER_INDEX_YUYV_TO_RGB_EXTERNAL 7
/* https://en.wikipedia.org/wiki/YCbCr, see study/color_space_transform_matrix.png */
@@ -248,6 +250,86 @@ static unsigned int load_graphics_shader_rgb(gsr_shader *shader, gsr_egl *egl, g
return 0;
}
static unsigned int load_graphics_shader_yuyv_to_rgb(gsr_shader *shader, gsr_egl *egl, gsr_color_graphics_uniforms *uniforms, bool external_texture) {
char vertex_shader[2048];
snprintf(vertex_shader, sizeof(vertex_shader),
"#version 300 es \n"
"in vec2 pos; \n"
"in vec2 texcoords; \n"
"out vec2 texcoords_out; \n"
"uniform vec2 offset; \n"
"uniform float rotation; \n"
"uniform mat2 rotation_matrix; \n"
"void main() \n"
"{ \n"
" texcoords_out = vec2(texcoords.x - 0.5, texcoords.y - 0.5) * rotation_matrix + vec2(0.5, 0.5); \n"
" gl_Position = vec4(offset.x, offset.y, 0.0, 0.0) + vec4(pos.x, pos.y, 0.0, 1.0); \n"
"} \n");
const char *main_code =
" vec2 resolution = vec2(textureSize(tex1, 0));\n"
" vec2 uv = texcoords_out * resolution;\n"
" float y = 0.0;\n"
" float u = 0.0;\n"
" float v = 0.0;\n"
" if(mod(uv.x, 2.0) < 1.0) {\n"
" vec3 this_color = texture(tex1, texcoords_out).rgb;\n"
" vec3 next_color = texture(tex1, texcoords_out + vec2(1.0, 0.0)/resolution).rgb;\n"
"\n"
" y = this_color.r;\n"
" u = this_color.g;\n"
" v = next_color.g;\n"
" } else {\n"
" vec3 this_color = texture(tex1, texcoords_out).rgb;\n"
" vec3 prev_color = texture(tex1, texcoords_out - vec2(1.0, 0.0)/resolution).rgb;\n"
"\n"
" y = this_color.r;\n"
" u = prev_color.g;\n"
" v = this_color.g;\n"
" }\n"
" FragColor = vec4(\n"
" y + 1.4065 * (v - 0.5),\n"
" y - 0.3455 * (u - 0.5) - 0.7169 * (v - 0.5),\n"
" y + 1.1790 * (u - 0.5),\n"
" 1.0);\n";
char fragment_shader[4096];
if(external_texture) {
snprintf(fragment_shader, sizeof(fragment_shader),
"#version 300 es \n"
"#extension GL_OES_EGL_image_external : enable \n"
"#extension GL_OES_EGL_image_external_essl3 : require \n"
"precision highp float; \n"
"in vec2 texcoords_out; \n"
"uniform samplerExternalOES tex1; \n"
"out vec4 FragColor; \n"
"void main() \n"
"{ \n"
"%s"
"} \n", main_code);
} else {
snprintf(fragment_shader, sizeof(fragment_shader),
"#version 300 es \n"
"precision highp float; \n"
"in vec2 texcoords_out; \n"
"uniform sampler2D tex1; \n"
"out vec4 FragColor; \n"
"void main() \n"
"{ \n"
"%s"
"} \n", main_code);
}
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0)
return -1;
gsr_shader_bind_attribute_location(shader, "pos", 0);
gsr_shader_bind_attribute_location(shader, "texcoords", 1);
uniforms->offset = egl->glGetUniformLocation(shader->program_id, "offset");
uniforms->rotation_matrix = egl->glGetUniformLocation(shader->program_id, "rotation_matrix");
return 0;
}
static int load_framebuffers(gsr_color_conversion *self) {
/* TODO: Only generate the necessary amount of framebuffers (self->params.num_destination_textures) */
const unsigned int draw_buffer = GL_COLOR_ATTACHMENT0;
@@ -314,7 +396,12 @@ static bool gsr_color_conversion_load_graphics_shaders(gsr_color_conversion *sel
}
case GSR_DESTINATION_COLOR_RGB8: {
if(load_graphics_shader_rgb(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_RGB], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_RGB], false) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y graphics shader\n");
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load RGB graphics shader\n");
return false;
}
if(load_graphics_shader_yuyv_to_rgb(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_YUYV_TO_RGB], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_YUYV_TO_RGB], false) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load YUYV to RGB graphics shader\n");
return false;
}
break;
@@ -328,19 +415,24 @@ static bool gsr_color_conversion_load_external_graphics_shaders(gsr_color_conver
case GSR_DESTINATION_COLOR_NV12:
case GSR_DESTINATION_COLOR_P010: {
if(load_graphics_shader_y(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_Y_EXTERNAL], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_Y_EXTERNAL], self->params.destination_color, self->params.color_range, true) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y graphics shader\n");
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y graphics shader (external)\n");
return false;
}
if(load_graphics_shader_uv(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_UV_EXTERNAL], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_UV_EXTERNAL], self->params.destination_color, self->params.color_range, true) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV graphics shader\n");
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV graphics shader (external)\n");
return false;
}
break;
}
case GSR_DESTINATION_COLOR_RGB8: {
if(load_graphics_shader_rgb(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_RGB_EXTERNAL], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_RGB_EXTERNAL], true) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y graphics shader\n");
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load RGB graphics shader (external)\n");
return false;
}
if(load_graphics_shader_yuyv_to_rgb(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_YUYV_TO_RGB_EXTERNAL], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_YUYV_TO_RGB_EXTERNAL], true) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load YUYV to RGB graphics shader (external)\n");
return false;
}
break;
@@ -539,35 +631,53 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig
self->params.egl->glBindBuffer(GL_ARRAY_BUFFER, self->vertex_buffer_object_id);
self->params.egl->glBufferSubData(GL_ARRAY_BUFFER, 0, 24 * sizeof(float), vertices);
switch(self->params.destination_color) {
case GSR_DESTINATION_COLOR_NV12:
case GSR_DESTINATION_COLOR_P010: {
self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[0]);
//cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); // TODO: Do this in a separate clear_ function. We want to do that when using multiple drm to create the final image (multiple monitors for example)
// TODO:
switch(source_color) {
case GSR_SOURCE_COLOR_RGB:
case GSR_SOURCE_COLOR_BGR: {
switch(self->params.destination_color) {
case GSR_DESTINATION_COLOR_NV12:
case GSR_DESTINATION_COLOR_P010: {
self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[0]);
//cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); // TODO: Do this in a separate clear_ function. We want to do that when using multiple drm to create the final image (multiple monitors for example)
int shader_index = external_texture ? GRAPHICS_SHADER_INDEX_Y_EXTERNAL : GRAPHICS_SHADER_INDEX_Y;
gsr_shader_use(&self->graphics_shaders[shader_index]);
self->params.egl->glUniformMatrix2fv(self->graphics_uniforms[shader_index].rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix);
self->params.egl->glUniform2f(self->graphics_uniforms[shader_index].offset, pos_norm.x, pos_norm.y);
self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6);
int shader_index = external_texture ? GRAPHICS_SHADER_INDEX_Y_EXTERNAL : GRAPHICS_SHADER_INDEX_Y;
gsr_shader_use(&self->graphics_shaders[shader_index]);
self->params.egl->glUniformMatrix2fv(self->graphics_uniforms[shader_index].rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix);
self->params.egl->glUniform2f(self->graphics_uniforms[shader_index].offset, pos_norm.x, pos_norm.y);
self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6);
if(self->params.num_destination_textures > 1) {
self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[1]);
//cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT);
if(self->params.num_destination_textures > 1) {
self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[1]);
//cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT);
shader_index = external_texture ? GRAPHICS_SHADER_INDEX_UV_EXTERNAL : GRAPHICS_SHADER_INDEX_UV;
gsr_shader_use(&self->graphics_shaders[shader_index]);
self->params.egl->glUniformMatrix2fv(self->graphics_uniforms[shader_index].rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix);
self->params.egl->glUniform2f(self->graphics_uniforms[shader_index].offset, pos_norm.x, pos_norm.y);
self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6);
shader_index = external_texture ? GRAPHICS_SHADER_INDEX_UV_EXTERNAL : GRAPHICS_SHADER_INDEX_UV;
gsr_shader_use(&self->graphics_shaders[shader_index]);
self->params.egl->glUniformMatrix2fv(self->graphics_uniforms[shader_index].rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix);
self->params.egl->glUniform2f(self->graphics_uniforms[shader_index].offset, pos_norm.x, pos_norm.y);
self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6);
}
break;
}
case GSR_DESTINATION_COLOR_RGB8: {
self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[0]);
//cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); // TODO: Do this in a separate clear_ function. We want to do that when using multiple drm to create the final image (multiple monitors for example)
const int shader_index = external_texture ? GRAPHICS_SHADER_INDEX_RGB_EXTERNAL : GRAPHICS_SHADER_INDEX_RGB;
gsr_shader_use(&self->graphics_shaders[shader_index]);
self->params.egl->glUniformMatrix2fv(self->graphics_uniforms[shader_index].rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix);
self->params.egl->glUniform2f(self->graphics_uniforms[shader_index].offset, pos_norm.x, pos_norm.y);
self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6);
break;
}
}
break;
}
case GSR_DESTINATION_COLOR_RGB8: {
case GSR_SOURCE_COLOR_YUYV: {
self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[0]);
//cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); // TODO: Do this in a separate clear_ function. We want to do that when using multiple drm to create the final image (multiple monitors for example)
const int shader_index = external_texture ? GRAPHICS_SHADER_INDEX_RGB_EXTERNAL : GRAPHICS_SHADER_INDEX_RGB;
const int shader_index = external_texture ? GRAPHICS_SHADER_INDEX_YUYV_TO_RGB_EXTERNAL : GRAPHICS_SHADER_INDEX_YUYV_TO_RGB;
gsr_shader_use(&self->graphics_shaders[shader_index]);
self->params.egl->glUniformMatrix2fv(self->graphics_uniforms[shader_index].rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix);
self->params.egl->glUniform2f(self->graphics_uniforms[shader_index].offset, pos_norm.x, pos_norm.y);

View File

@@ -282,7 +282,6 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
{ (void**)&self->glTexParameterfv, "glTexParameterfv" },
{ (void**)&self->glTexImage2D, "glTexImage2D" },
{ (void**)&self->glTexSubImage2D, "glTexSubImage2D" },
{ (void**)&self->glTexStorage2D, "glTexStorage2D" },
{ (void**)&self->glGetTexImage, "glGetTexImage" },
{ (void**)&self->glGenFramebuffers, "glGenFramebuffers" },
{ (void**)&self->glBindFramebuffer, "glBindFramebuffer" },
@@ -330,7 +329,7 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
{ (void**)&self->glDebugMessageCallback, "glDebugMessageCallback" },
{ (void**)&self->glScissor, "glScissor" },
{ (void**)&self->glReadPixels, "glReadPixels" },
{ (void**)&self->glMapBuffer, "glMapBuffer" },
{ (void**)&self->glMapBufferRange, "glMapBufferRange" },
{ (void**)&self->glUnmapBuffer, "glUnmapBuffer" },
{ (void**)&self->glGetIntegerv, "glGetIntegerv" },

View File

@@ -2301,7 +2301,7 @@ static gsr_capture* create_capture_impl(const args_parser &arg_parser, gsr_egl *
gsr_capture_v4l2_params v4l2_params;
memset(&v4l2_params, 0, sizeof(v4l2_params));
v4l2_params.egl = egl;
v4l2_params.output_resolution = {0, 0};
v4l2_params.output_resolution = arg_parser.output_resolution;
v4l2_params.device_path = capture_source.name.c_str();
v4l2_params.pixfmt = capture_source.v4l2_pixfmt;
v4l2_params.fps = arg_parser.fps;
@@ -2505,9 +2505,9 @@ static void capture_image_to_file(args_parser &arg_parser, gsr_egl *egl, gsr_win
fprintf(stderr, "gsr error: failed to get kms, error: %d (%s)\n", kms_response.result, kms_response.err_msg);
}
bool all_sources_captured = true;
should_stop_error = false;
for(VideoSource &video_source : video_sources) {
should_stop_error = false;
gsr_capture_tick(video_source.capture);
if(gsr_capture_should_stop(video_source.capture, &should_stop_error)) {
running = 0;
break;
@@ -2524,6 +2524,7 @@ static void capture_image_to_file(args_parser &arg_parser, gsr_egl *egl, gsr_win
gsr_color_conversion_clear(&color_conversion);
}
bool all_sources_captured = true;
for(VideoSource &video_source : video_sources) {
// It can fail, for example when capturing portal and the target is a monitor that hasn't been updated.
// This can also happen for example if the system suspends and the monitor to capture's framebuffer is gone, or if the target window disappeared.

View File

@@ -631,9 +631,7 @@ unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal
unsigned int texture_id = 0;
egl->glGenTextures(1, &texture_id);
egl->glBindTexture(GL_TEXTURE_2D, texture_id);
// TODO:
egl->glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
//egl->glTexStorage2D(GL_TEXTURE_2D, 1, internal_format, width, height);
egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);