summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-02-20 13:33:20 -0500
committerpommicket <pommicket@gmail.com>2025-02-20 13:33:20 -0500
commit72bec683d1c738a882e19fa91cd2c85f6a854cd9 (patch)
tree4c53829b3025489fab7970320c64b67bf054f3c9
parent6e46884abd0996b46c61953dd0bcbc5466db427e (diff)
add NV12/21 supoprt
-rw-r--r--camera.c48
-rw-r--r--main.c11
2 files changed, 56 insertions, 3 deletions
diff --git a/camera.c b/camera.c
index acc63bc..c96dc73 100644
--- a/camera.c
+++ b/camera.c
@@ -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,
diff --git a/main.c b/main.c
index 9164b83..e29cb9a 100644
--- a/main.c
+++ b/main.c
@@ -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\