diff options
author | pommicket <pommicket@gmail.com> | 2025-02-20 13:33:20 -0500 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-02-20 13:33:20 -0500 |
commit | 72bec683d1c738a882e19fa91cd2c85f6a854cd9 (patch) | |
tree | 4c53829b3025489fab7970320c64b67bf054f3c9 | |
parent | 6e46884abd0996b46c61953dd0bcbc5466db427e (diff) |
add NV12/21 supoprt
-rw-r--r-- | camera.c | 48 | ||||
-rw-r--r-- | main.c | 11 |
2 files changed, 56 insertions, 3 deletions
@@ -147,6 +147,8 @@ bool pix_fmt_supported(uint32_t pixfmt) { case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: return true; } return false; @@ -347,9 +349,10 @@ static uint8_t *curr_frame_rgb24(Camera *camera) { } int32_t frame_width = camera_frame_width(camera); int32_t frame_height = camera_frame_height(camera); - const uint8_t *in = curr_frame, *in_y = NULL, *in_cb = NULL, *in_cr = NULL; + const uint8_t *in = curr_frame, *in_y = NULL, *in_cb = NULL, *in_cr = NULL, *in_cbcr = NULL; uint8_t *out = camera->decompression_buf; - switch (camera_pixel_format(camera)) { + uint32_t pixfmt = camera_pixel_format(camera); + switch (pixfmt) { case V4L2_PIX_FMT_BGR24: { for (int32_t y = 0; y < frame_height; y++) { for (int32_t x = 0; x < frame_width; x++) { @@ -425,6 +428,30 @@ static uint8_t *curr_frame_rgb24(Camera *camera) { } } return camera->decompression_buf; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + in_y = curr_frame; + in_cbcr = curr_frame + (size_t)frame_width * (size_t)frame_height; + for (int32_t row = 0; row < frame_height; row++) { + for (int32_t col = 0; col < frame_width; col++) { + float y = (float)(*in_y++) * (1.0f / 255.0f); + float cb = (float)(in_cbcr[pixfmt == V4L2_PIX_FMT_NV21]) * (1.0f / 255.0f); + float cr = (float)(in_cbcr[pixfmt == V4L2_PIX_FMT_NV12]) * (1.0f / 255.0f); + if (col % 2 == 1) { + in_cbcr += 2; + } + float rgb[3]; + ycbcr_ITU_R_601_to_rgb(y, cb, cr, rgb); + *out++ = (uint8_t)roundf(rgb[0] * 255); + *out++ = (uint8_t)roundf(rgb[1] * 255); + *out++ = (uint8_t)roundf(rgb[2] * 255); + } + if (row % 2 == 0) { + // go back to start of cbcr row + in_cbcr -= frame_width; + } + } + return camera->decompression_buf; } assert(false); return NULL; @@ -598,10 +625,13 @@ void camera_update_gl_textures(Camera *camera, const GLuint textures[3]) { break; case V4L2_PIX_FMT_YUV420: if (camera->frame_bytes_set >= (size_t)frame_width * (size_t)frame_height * 3 / 2) { + // Y plane gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RED, frame_width, frame_height, 0, GL_RED, GL_UNSIGNED_BYTE, curr_frame); + // Cb plane gl.BindTexture(GL_TEXTURE_2D, textures[1]); gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RED, frame_width / 2, frame_height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, curr_frame + (size_t)frame_width * (size_t)frame_height); + // Cr plane gl.BindTexture(GL_TEXTURE_2D, textures[2]); gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RED, frame_width / 2, frame_height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, curr_frame + (size_t)frame_width * (size_t)frame_height * 5 / 4); @@ -610,15 +640,29 @@ void camera_update_gl_textures(Camera *camera, const GLuint textures[3]) { case V4L2_PIX_FMT_YVU420: if (camera->frame_bytes_set >= (size_t)frame_width * (size_t)frame_height * 3 / 2) { // same as above, but swap textures[1] and textures[2] so that we only have to handle one case in the shader + // Y plane gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RED, frame_width, frame_height, 0, GL_RED, GL_UNSIGNED_BYTE, curr_frame); + // Cr plane gl.BindTexture(GL_TEXTURE_2D, textures[2]); gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RED, frame_width / 2, frame_height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, curr_frame + (size_t)frame_width * (size_t)frame_height); + // Cb plane gl.BindTexture(GL_TEXTURE_2D, textures[1]); gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RED, frame_width / 2, frame_height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, curr_frame + (size_t)frame_width * (size_t)frame_height * 5 / 4); } break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + if (camera->frame_bytes_set >= (size_t)frame_width * (size_t)frame_height * 3 / 2) { + // Y plane + gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RED, frame_width, frame_height, 0, GL_RED, GL_UNSIGNED_BYTE, curr_frame); + // CbCr or CrCb plane + gl.BindTexture(GL_TEXTURE_2D, textures[1]); + gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RG, frame_width / 2, frame_height / 2, 0, GL_RG, GL_UNSIGNED_BYTE, + curr_frame + (size_t)frame_width * (size_t)frame_height); + } + break; case V4L2_PIX_FMT_MJPEG: { // "motion jpeg" is actually just a series of jpegs gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, frame_width, frame_height, 0, GL_RGB, GL_UNSIGNED_BYTE, @@ -1,6 +1,5 @@ /* TODO --add support for more pixfmts -adjustable camera framerate -view previous pictures (thumbnails) -click in menus @@ -440,6 +439,16 @@ void main() {\n\ float cr = texture2D(u_sampler3, tex_coord).x;\n\ color = ycbcr_ITU_R_601_to_rgb(vec3(y,cb,cr));\n\ } break;\n\ + case 0x3231564e: {// YUV 4:2:0 with a Y plane and a UV plane\n\ + float y = texture2D(u_sampler, tex_coord).x;\n\ + vec2 cbcr = texture2D(u_sampler2, tex_coord).xy;\n\ + color = ycbcr_ITU_R_601_to_rgb(vec3(y,cbcr));\n\ + } break;\n\ + case 0x3132564e: {// YVU 4:2:0 with a Y plane and a VU plane\n\ + float y = texture2D(u_sampler, tex_coord).x;\n\ + vec2 cbcr = texture2D(u_sampler2, tex_coord).yx;\n\ + color = ycbcr_ITU_R_601_to_rgb(vec3(y,cbcr));\n\ + } break;\n\ default:\n\ color = texture2D(u_sampler, tex_coord).xyz;\n\ break;\n\ |