summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-02-24 17:04:16 -0500
committerpommicket <pommicket@gmail.com>2025-02-25 15:16:08 -0500
commitb164809f0ff29210344a0dbc8310d5a4916ddc70 (patch)
tree3c326c82fb9e2555ea5d7632ac0a29facba18bb1
parent1a67ba51c7a169348e9e0be5db288d8046202a17 (diff)
fix various things, separate video & image resolution settings
-rw-r--r--camera.c35
-rw-r--r--camera.h2
-rw-r--r--main.c68
3 files changed, 69 insertions, 36 deletions
diff --git a/camera.c b/camera.c
index 63e1d2d..2d64f64 100644
--- a/camera.c
+++ b/camera.c
@@ -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;
}
diff --git a/camera.h b/camera.h
index 9029b36..8a56e07 100644
--- a/camera.h
+++ b/camera.h
@@ -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);
diff --git a/main.c b/main.c
index bc0dbb1..e425de0 100644
--- a/main.c
+++ b/main.c
@@ -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;