diff options
author | pommicket <pommicket@gmail.com> | 2025-02-24 17:04:16 -0500 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-02-25 15:16:08 -0500 |
commit | b164809f0ff29210344a0dbc8310d5a4916ddc70 (patch) | |
tree | 3c326c82fb9e2555ea5d7632ac0a29facba18bb1 | |
parent | 1a67ba51c7a169348e9e0be5db288d8046202a17 (diff) |
fix various things, separate video & image resolution settings
-rw-r--r-- | camera.c | 35 | ||||
-rw-r--r-- | camera.h | 2 | ||||
-rw-r--r-- | main.c | 68 |
3 files changed, 69 insertions, 36 deletions
@@ -238,19 +238,28 @@ uint32_t *camera_get_pixfmts(Camera *camera) { arr_qsort(available, uint32_cmp_qsort); return available; } -PictureFormat camera_closest_resolution(Camera *camera, uint32_t pixfmt, int32_t desired_width, int32_t desired_height) { - PictureFormat best_format = camera->best_format; // default if there's nothing with the desired pixfmt + +PictureFormat camera_closest_picfmt(Camera *camera, PictureFormat desired) { + PictureFormat best_format = {0}; int32_t best_score = INT32_MIN; arr_foreach_ptr(camera->formats, const PictureFormat, fmt) { - if (fmt->pixfmt != pixfmt) { - continue; + int32_t score = 0; + if (fmt->pixfmt != desired.pixfmt) { + score -= INT32_MAX / 4; + } + if (desired.width == 0 && desired.height == 0) { + // go for largest resolution if none is specified + score += fmt->width + fmt->height; + } else { + // closest difference in resolution + score -= abs(fmt->width - desired.width) + abs(fmt->height - desired.height); } - int32_t score = -abs(fmt->width - desired_width) + abs(fmt->height - desired_height); if (score >= best_score) { best_score = score; best_format = *fmt; } } + assert(best_format.pixfmt); return best_format; } @@ -288,6 +297,10 @@ TODO: test me with a camera that supports userptr i/o return true;*/ } static bool camera_stop_io(Camera *camera) { + if (v4l2_ioctl(camera->fd, VIDIOC_STREAMOFF, + (enum v4l2_buf_type[1]) { V4L2_BUF_TYPE_VIDEO_CAPTURE }) != 0) { + perror("v4l2_ioctl VIDIOC_STREAMOFF"); + } // Just doing VIDIOC_STREAMOFF doesn't seem to be enough to prevent EBUSY. // (Even if we dequeue all buffers afterwards) // Re-opening doesn't seem to be necessary for read-based access for me, @@ -731,6 +744,9 @@ bool camera_set_format(Camera *camera, PictureFormat picfmt, CameraAccessMethod // no changes needed return true; } + assert(picfmt.pixfmt); + assert(picfmt.width); + assert(picfmt.height); camera->any_frames = false; camera->access_method = access; for (int i = 0; i < camera->buffer_count; i++) { @@ -796,13 +812,6 @@ bool camera_open(Camera *camera, PictureFormat desired_format) { perror("v4l2_ioctl"); return false; } - int32_t desired_width = desired_format.width; - int32_t desired_height = desired_format.height; - uint32_t desired_pixfmt = desired_format.pixfmt; - if (!desired_width) desired_width = camera->best_format.width; - if (!desired_height) desired_height = camera->best_format.height; - if (!desired_pixfmt) desired_pixfmt = camera->best_format.pixfmt; - desired_format = camera_closest_resolution(camera, desired_pixfmt, desired_width, desired_height); camera_set_format(camera, desired_format, camera->access_method, true); return true; } @@ -946,7 +955,7 @@ bool camera_copy_to_av_frame(Camera *camera, struct AVFrame *frame_out) { || frame_out->format != AV_PIX_FMT_YUV420P) { static atomic_flag warned = ATOMIC_FLAG_INIT; if (!atomic_flag_test_and_set_explicit(&warned, memory_order_relaxed)) { - fprintf(stderr, "%s: Bad picture format.", __func__); + fprintf(stderr, "%s: Bad picture format.\n", __func__); } return false; } @@ -107,7 +107,7 @@ int picture_format_cmp_qsort(const void *av, const void *bv); const char *pixfmt_to_string(uint32_t pixfmt); PictureFormat *camera_get_resolutions_with_pixfmt(Camera *camera, uint32_t pixfmt); uint32_t *camera_get_pixfmts(Camera *camera); -PictureFormat camera_closest_resolution(Camera *camera, uint32_t pixfmt, int32_t desired_width, int32_t desired_height); +PictureFormat camera_closest_picfmt(Camera *camera, PictureFormat picfmt); int32_t camera_frame_width(Camera *camera); int32_t camera_frame_height(Camera *camera); PictureFormat camera_picture_format(Camera *camera); @@ -72,7 +72,7 @@ static const char *const image_format_names[IMG_FMT_COUNT] = {"JPEG", "PNG"}; static const char *const image_format_extensions[IMG_FMT_COUNT] = {"jpg", "png"}; typedef enum { - MODE_PICTURE, + MODE_IMAGE, MODE_VIDEO, MODE_COUNT, @@ -82,7 +82,8 @@ typedef struct { Hash *camera_precedence; uint32_t pixel_format; int timer; - int32_t resolution[2]; + int32_t image_resolution[2]; + int32_t video_resolution[2]; ImageFormat image_format; char *output_dir; } Settings; @@ -132,14 +133,21 @@ static void APIENTRY gl_message_callback(GLenum source, GLenum type, unsigned in } #endif -static PictureFormat settings_picture_format(Settings *settings) { +static PictureFormat settings_desired_picture_format(State *state) { + Settings *settings = &state->settings; return (PictureFormat) { - .width = settings->resolution[0], - .height = settings->resolution[1], + .width = state->mode == MODE_VIDEO ? settings->video_resolution[0] : settings->image_resolution[0], + .height = state->mode == MODE_VIDEO ? settings->video_resolution[1] : settings->image_resolution[1], .pixfmt = settings->pixel_format }; } + +static PictureFormat settings_picture_format_for_camera(State *state, Camera *camera) { + PictureFormat picfmt = settings_desired_picture_format(state); + return camera_closest_picfmt(camera, picfmt); +} + // compile a GLSL shader GLuint gl_compile_shader(char error_buf[256], const char *code, GLenum shader_type) { GLuint shader = gl.CreateShader(shader_type); @@ -294,6 +302,7 @@ static void menu_select(State *state) { state->menu_needs_rerendering = true; // set menu_sel PictureFormat *resolutions = camera_get_resolutions_with_pixfmt(state->camera, camera_pixel_format(state->camera)); + state->menu_sel[MENU_RESOLUTION] = 0; arr_foreach_ptr(resolutions, PictureFormat, resolution) { if (resolution->width == camera_frame_width(state->camera) && resolution->height == camera_frame_height(state->camera)) { @@ -314,7 +323,7 @@ static void menu_select(State *state) { } } break; - case MENU_OPT_PIXFMT: if (state->camera) { + case MENU_OPT_PIXFMT: if (state->camera && state->mode != MODE_VIDEO) { state->curr_menu = MENU_PIXFMT; state->menu_needs_rerendering = true; // set menu_sel @@ -346,8 +355,16 @@ static void menu_select(State *state) { return; } PictureFormat *resolutions = camera_get_resolutions_with_pixfmt(state->camera, camera_pixel_format(state->camera)); + PictureFormat resolution = resolutions[sel-1]; + if (state->mode == MODE_VIDEO) { + settings->video_resolution[0] = resolution.width; + settings->video_resolution[1] = resolution.height; + } else { + settings->image_resolution[0] = resolution.width; + settings->image_resolution[1] = resolution.height; + } camera_set_format(state->camera, - resolutions[sel-1], + resolution, camera_access_method(state->camera), false); arr_free(resolutions); @@ -365,7 +382,8 @@ static void menu_select(State *state) { } else { camera_close(state->camera); state->camera = new_camera; - if (camera_open(state->camera, settings_picture_format(settings))) { + PictureFormat picfmt = settings_picture_format_for_camera(state, state->camera); + if (camera_open(state->camera, picfmt)) { // put at highest precedence move_to_highest_precedence(&state->settings, state->camera); } else { @@ -381,8 +399,8 @@ static void menu_select(State *state) { state->menu_needs_rerendering = true; return; } - uint32_t pixfmt = pixfmts[sel-1]; - PictureFormat new_picfmt = camera_closest_resolution(state->camera, pixfmt, settings->resolution[0], settings->resolution[1]); + settings->pixel_format = pixfmts[sel-1]; + PictureFormat new_picfmt = settings_picture_format_for_camera(state, state->camera); arr_free(pixfmts); camera_set_format(state->camera, new_picfmt, @@ -432,7 +450,7 @@ static void select_camera(State *state) { } state->camera = state->cameras[camera_idx]; } - if (camera_open(state->camera, settings_picture_format(settings))) { + if (camera_open(state->camera, settings_picture_format_for_camera(state, state->camera))) { bool already_there = false; arr_foreach_ptr(settings->camera_precedence, Hash, h) { if (hash_eq(*h, camera_hash(state->camera))) { @@ -580,7 +598,7 @@ static bool take_picture(State *state) { snprintf(path + strlen(path), sizeof path - strlen(path), ".%s", extension); bool success = false; switch (state->mode) { - case MODE_PICTURE: + case MODE_IMAGE: switch (settings->image_format) { case IMG_FMT_JPEG: success = camera_save_jpg(state->camera, path, 90); @@ -1016,20 +1034,26 @@ void main() {\n\ } break; case SDLK_TAB: + if (state->curr_menu) break; if (video_is_recording(state->video)) break; state->mode = (state->mode + 1) % MODE_COUNT; switch (state->mode) { - case MODE_PICTURE: - // TODO: go back to normal settings - break; - case MODE_VIDEO: - // TODO: configurable width/height - camera_set_format(state->camera, (PictureFormat) { - .width = 1280, - .height = 720, - .pixfmt = V4L2_PIX_FMT_YUV420 - }, CAMERA_ACCESS_MMAP, false); + case MODE_IMAGE: + camera_set_format(state->camera, + settings_picture_format_for_camera(state, state->camera), + camera_access_method(state->camera), false); break; + case MODE_VIDEO: { + PictureFormat desired = settings_desired_picture_format(state); + desired.pixfmt = V4L2_PIX_FMT_YUV420; + // default to 720p for video + if (!desired.width) desired.width = 1280; + if (!desired.height) desired.height = 720; + PictureFormat picfmt = camera_closest_picfmt(state->camera, desired); + // force V4L2 to do the conversion if we have to + picfmt.pixfmt = V4L2_PIX_FMT_YUV420; + camera_set_format(state->camera, picfmt, camera_access_method(state->camera), false); + } break; case MODE_COUNT: assert(false); break; |