Add libmegapixels
This commit is contained in:
@@ -5,15 +5,94 @@ set(CMAKE_C_STANDARD 11)
|
||||
|
||||
# Use the package PkgConfig to detect GTK+ headers/library files
|
||||
FIND_PACKAGE(PkgConfig REQUIRED)
|
||||
PKG_CHECK_MODULES(GTK3 REQUIRED gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GTK4 REQUIRED gtk4)
|
||||
PKG_CHECK_MODULES(FEEDBACK REQUIRED libfeedback-0.0)
|
||||
PKG_CHECK_MODULES(TIFF REQUIRED libtiff-4)
|
||||
PKG_CHECK_MODULES(ZBAR REQUIRED zbar)
|
||||
PKG_CHECK_MODULES(EPOXY REQUIRED epoxy)
|
||||
PKG_CHECK_MODULES(MEGAPIXELS REQUIRED libmegapixels)
|
||||
PKG_CHECK_MODULES(XLIB REQUIRED x11)
|
||||
PKG_CHECK_MODULES(XRANDR REQUIRED xrandr)
|
||||
PKG_CHECK_MODULES(WAYLAND REQUIRED wayland-client)
|
||||
|
||||
# Setup CMake to use GTK+, tell the compiler where to look for headers
|
||||
# and to the linker where to look for libraries
|
||||
INCLUDE_DIRECTORIES(${GTK3_INCLUDE_DIRS})
|
||||
LINK_DIRECTORIES(${GTK3_LIBRARY_DIRS})
|
||||
INCLUDE_DIRECTORIES(
|
||||
${GTK4_INCLUDE_DIRS}
|
||||
${FEEDBACK_INCLUDE_DIRS}
|
||||
${TIFF_INCLUDE_DIRS}
|
||||
${ZBAR_INCLUDE_DIRS}
|
||||
${EPOXY_INCLUDE_DIRS}
|
||||
${MEGAPIXELS_INCLUDE_DIRS}
|
||||
${XLIB_INCLUDE_DIRS}
|
||||
${XRANDR_INCLUDE_DIRS}
|
||||
${WAYLAND_INCLUDE_DIRS}
|
||||
)
|
||||
LINK_DIRECTORIES(
|
||||
${GTK4_LIBRARY_DIRS}
|
||||
${FEEDBACK_LIBRARY_DIRS}
|
||||
${TIFF_LIBRARY_DIRS}
|
||||
${ZBAR_LIBRARY_DIRS}
|
||||
${EPOXY_LIBRARY_DIRS}
|
||||
${MEGAPIXELS_LIBRARY_DIRS}
|
||||
${XLIB_LIBRARY_DIRS}
|
||||
${XRANDR_LIBRARY_DIRS}
|
||||
${WAYLAND_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
# Add other flags to the compiler
|
||||
ADD_DEFINITIONS(${GTK3_CFLAGS_OTHER})
|
||||
ADD_DEFINITIONS(
|
||||
${GTK4_CFLAGS_OTHER}
|
||||
${FEEDBACK_CFLAGS_OTHER}
|
||||
${TIFF_CFLAGS_OTHER}
|
||||
${ZBAR_CFLAGS_OTHER}
|
||||
${EPOXY_CFLAGS_OTHER}
|
||||
${MEGAPIXELS_CFLAGS_OTHER}
|
||||
${XLIB_CFLAGS_OTHER}
|
||||
${XRANDR_CFLAGS_OTHER}
|
||||
${WAYLAND_CFLAGS_OTHER}
|
||||
)
|
||||
|
||||
add_executable(megapixels main.c ini.c ini.h bayer.c bayer.h)
|
||||
target_link_libraries(megapixels ${GTK3_LIBRARIES})
|
||||
find_program(GLIB_COMPILE_RESOURCES NAMES glib-compile-resources REQUIRED)
|
||||
set(GRESOURCE_C megapixels.gresource.c)
|
||||
set(GRESOURCE_XML data/org.postmarketos.Megapixels.gresource.xml)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data
|
||||
COMMAND ${GLIB_COMPILE_RESOURCES}
|
||||
ARGS
|
||||
--generate-source
|
||||
--target=${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C}
|
||||
../${GRESOURCE_XML}
|
||||
VERBATIM
|
||||
MAIN_DEPENDENCY ${GRESOURCE_XML}
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
dummy-resource
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C}
|
||||
)
|
||||
|
||||
file(GLOB srcs src/*.c src/*h)
|
||||
add_executable(megapixels-gtk ${srcs} ${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C})
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C}
|
||||
PROPERTIES GENERATED TRUE
|
||||
)
|
||||
add_dependencies(megapixels-gtk dummy-resource)
|
||||
target_link_libraries(megapixels-gtk
|
||||
${GTK4_LIBRARIES}
|
||||
${FEEDBACK_LIBRARIES}
|
||||
${TIFF_LIBRARIES}
|
||||
${ZBAR_LIBRARIES}
|
||||
${EPOXY_LIBRARIES}
|
||||
${MEGAPIXELS_LIBRARIES}
|
||||
${XLIB_LIBRARIES}
|
||||
${XRANDR_LIBRARIES}
|
||||
${WAYLAND_LIBRARIES}
|
||||
)
|
||||
|
||||
add_compile_definitions(VERSION="${PROJECT_VERSION}")
|
||||
add_compile_definitions(SYSCONFDIR="/etc")
|
||||
add_compile_definitions(DATADIR="/usr/share")
|
1
include/libmegapixels.h
Normal file
1
include/libmegapixels.h
Normal file
@@ -0,0 +1 @@
|
||||
extern int libmegapixels_find_config(char *configfile);
|
3
libmegapixels/CMakeLists.txt
Normal file
3
libmegapixels/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
add_library(megapixels SHARED libmegapixels/findconfig.c ../include/libmegapixels.h)
|
||||
set_target_properties(megapixels PROPERTIES VERSION 1)
|
||||
set_target_properties(megapixels PROPERTIES SOVERSION 1)
|
60
libmegapixels/findconfig.c
Normal file
60
libmegapixels/findconfig.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef SYSCONFDIR
|
||||
#define SYSCONFDIR "/etc"
|
||||
#endif
|
||||
#ifndef DATADIR
|
||||
#define DATADIR "/usr/share"
|
||||
#endif
|
||||
|
||||
static int
|
||||
find_device_by_model(char *conffile, char *model)
|
||||
{
|
||||
// Check config/%model.conf in the current working directory
|
||||
sprintf(conffile, "config/%s.conf", model);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check user overridden /etc/megapixels/config/%model.conf
|
||||
sprintf(conffile, "%s/megapixels/config/%s.conf", SYSCONFDIR, model);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check packaged /usr/share/megapixels/config/%model.conf
|
||||
sprintf(conffile, "%s/megapixels/config/%s.conf", DATADIR, model);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
libmegapixels_find_config(char *configfile)
|
||||
{
|
||||
char model[512];
|
||||
FILE *fp;
|
||||
|
||||
if (access("/proc/device-tree/compatible", F_OK) == -1) {
|
||||
return -1;
|
||||
}
|
||||
fp = fopen("/proc/device-tree/compatible", "r");
|
||||
char *modelptr = model;
|
||||
while (1) {
|
||||
int c = fgetc(fp);
|
||||
if (c == EOF) {
|
||||
*(modelptr) = '\0';
|
||||
return find_device_by_model(configfile, model);
|
||||
}
|
||||
*(modelptr++) = (char)c;
|
||||
if (c == 0) {
|
||||
if (find_device_by_model(configfile, model)) {
|
||||
return 0;
|
||||
}
|
||||
modelptr = model;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
24
libmegapixels/meson.build
Normal file
24
libmegapixels/meson.build
Normal file
@@ -0,0 +1,24 @@
|
||||
libmegapixels_sources = [
|
||||
'findconfig.c',
|
||||
]
|
||||
|
||||
|
||||
# We use libtool-version numbers because it's easier to understand.
|
||||
# Before making a release, the libruntime_so_*
|
||||
# numbers should be modified. The components are of the form C:R:A.
|
||||
# a) If binary compatibility has been broken (eg removed or changed interfaces)
|
||||
# change to C+1:0:0.
|
||||
# b) If interfaces have been changed or added, but binary compatibility has
|
||||
# been preserved, change to C+1:0:A+1
|
||||
# c) If the interface is the same as the previous version, change to C:R+1:A
|
||||
libmegapixels_lt_c=1
|
||||
libmegapixels_lt_r=0
|
||||
libmegapixels_lt_a=0
|
||||
|
||||
libmegapixels_so_version = '@0@.@1@.@2@'.format((libmegapixels_lt_c - libmegapixels_lt_a),
|
||||
libmegapixels_lt_a,
|
||||
libmegapixels_lt_r)
|
||||
|
||||
libmegapixels = shared_library('libmegapixels', libmegapixels_sources,
|
||||
version:libmegapixels_so_version,
|
||||
install: true)
|
25
meson.build
25
meson.build
@@ -9,6 +9,8 @@ threads = dependency('threads')
|
||||
# gl = dependency('gl')
|
||||
epoxy = dependency('epoxy')
|
||||
|
||||
libmp = dependency('libmegapixels')
|
||||
|
||||
# We only build in support for Wayland/X11 if GTK did so
|
||||
optdeps = []
|
||||
if gtkdep.get_variable('targets').contains('wayland')
|
||||
@@ -48,8 +50,6 @@ endif
|
||||
|
||||
executable('megapixels',
|
||||
'src/camera.c',
|
||||
'src/camera_config.c',
|
||||
'src/device.c',
|
||||
'src/flash.c',
|
||||
'src/gl_util.c',
|
||||
'src/gles2_debayer.c',
|
||||
@@ -57,14 +57,13 @@ executable('megapixels',
|
||||
'src/io_pipeline.c',
|
||||
'src/main.c',
|
||||
'src/matrix.c',
|
||||
'src/mode.c',
|
||||
'src/pipeline.c',
|
||||
'src/process_pipeline.c',
|
||||
'src/zbar_pipeline.c',
|
||||
'src/dcp.c',
|
||||
resources,
|
||||
include_directories: 'src/',
|
||||
dependencies: [gtkdep, libfeedback, libm, tiff, zbar, threads, epoxy] + optdeps,
|
||||
dependencies: [gtkdep, libfeedback, libm, tiff, zbar, threads, epoxy, libmp] + optdeps,
|
||||
install: true,
|
||||
link_args: '-Wl,-ldl')
|
||||
|
||||
@@ -80,24 +79,6 @@ install_data(
|
||||
],
|
||||
install_dir: get_option('datadir') / 'megapixels/config/')
|
||||
|
||||
# Tools
|
||||
executable('megapixels-list-devices',
|
||||
'tools/list_devices.c',
|
||||
'src/device.c',
|
||||
'src/mode.c',
|
||||
include_directories: 'src/',
|
||||
dependencies: [gtkdep],
|
||||
install: true)
|
||||
|
||||
executable('megapixels-camera-test',
|
||||
'tools/camera_test.c',
|
||||
'src/camera.c',
|
||||
'src/device.c',
|
||||
'src/mode.c',
|
||||
include_directories: 'src/',
|
||||
dependencies: [gtkdep],
|
||||
install: true)
|
||||
|
||||
# Formatting
|
||||
clang_format = find_program('clang-format-14', required: false)
|
||||
if clang_format.found()
|
||||
|
514
src/camera.c
514
src/camera.c
@@ -1,9 +1,9 @@
|
||||
#include "camera.h"
|
||||
#include "mode.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <libmegapixels.h>
|
||||
#include <linux/v4l2-subdev.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
@@ -11,9 +11,6 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAX_VIDEO_BUFFERS 20
|
||||
#define MAX_BG_TASKS 8
|
||||
|
||||
static void
|
||||
errno_printerr(const char *s)
|
||||
{
|
||||
@@ -37,12 +34,7 @@ struct video_buffer {
|
||||
};
|
||||
|
||||
struct _MPCamera {
|
||||
int video_fd;
|
||||
int subdev_fd;
|
||||
int bridge_fd;
|
||||
|
||||
bool has_set_mode;
|
||||
MPMode current_mode;
|
||||
libmegapixels_camera *camera;
|
||||
|
||||
struct video_buffer buffers[MAX_VIDEO_BUFFERS];
|
||||
uint32_t num_buffers;
|
||||
@@ -54,13 +46,12 @@ struct _MPCamera {
|
||||
};
|
||||
|
||||
MPCamera *
|
||||
mp_camera_new(int video_fd, int subdev_fd, int bridge_fd)
|
||||
mp_camera_new(libmegapixels_camera *camera)
|
||||
{
|
||||
g_return_val_if_fail(video_fd != -1, NULL);
|
||||
|
||||
libmegapixels_open(camera);
|
||||
// Query capabilities
|
||||
struct v4l2_capability cap;
|
||||
if (xioctl(video_fd, VIDIOC_QUERYCAP, &cap) == -1) {
|
||||
if (xioctl(camera->video_fd, VIDIOC_QUERYCAP, &cap) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -73,31 +64,14 @@ mp_camera_new(int video_fd, int subdev_fd, int bridge_fd)
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
libmegapixels_close(camera);
|
||||
|
||||
MPCamera *camera = malloc(sizeof(MPCamera));
|
||||
camera->video_fd = video_fd;
|
||||
camera->subdev_fd = subdev_fd;
|
||||
camera->bridge_fd = bridge_fd;
|
||||
camera->has_set_mode = false;
|
||||
camera->num_buffers = 0;
|
||||
camera->use_mplane = use_mplane;
|
||||
memset(camera->child_bg_pids,
|
||||
0,
|
||||
sizeof(camera->child_bg_pids[0]) * MAX_BG_TASKS);
|
||||
return camera;
|
||||
}
|
||||
|
||||
void
|
||||
mp_camera_free(MPCamera *camera)
|
||||
{
|
||||
mp_camera_wait_bg_tasks(camera);
|
||||
|
||||
g_warn_if_fail(camera->num_buffers == 0);
|
||||
if (camera->num_buffers != 0) {
|
||||
mp_camera_stop_capture(camera);
|
||||
}
|
||||
|
||||
free(camera);
|
||||
MPCamera *cam = malloc(sizeof(MPCamera));
|
||||
cam->camera = camera;
|
||||
cam->num_buffers = 0;
|
||||
cam->use_mplane = use_mplane;
|
||||
memset(cam->child_bg_pids, 0, sizeof(cam->child_bg_pids[0]) * MAX_BG_TASKS);
|
||||
return cam;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -180,22 +154,10 @@ mp_camera_check_task_complete(MPCamera *camera, pid_t pid)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
mp_camera_is_subdev(MPCamera *camera)
|
||||
{
|
||||
return camera->subdev_fd != -1;
|
||||
}
|
||||
|
||||
int
|
||||
mp_camera_get_video_fd(MPCamera *camera)
|
||||
{
|
||||
return camera->video_fd;
|
||||
}
|
||||
|
||||
int
|
||||
mp_camera_get_subdev_fd(MPCamera *camera)
|
||||
{
|
||||
return camera->subdev_fd;
|
||||
return camera->camera->video_fd;
|
||||
}
|
||||
|
||||
static enum v4l2_buf_type
|
||||
@@ -207,151 +169,9 @@ get_buf_type(MPCamera *camera)
|
||||
return V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
}
|
||||
|
||||
static bool
|
||||
camera_mode_impl(MPCamera *camera, int request, MPMode *mode)
|
||||
{
|
||||
uint32_t pixfmt = mp_pixel_format_to_v4l_pixel_format(mode->pixel_format);
|
||||
struct v4l2_format fmt = {};
|
||||
if (camera->use_mplane) {
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
fmt.fmt.pix_mp.width = mode->width;
|
||||
fmt.fmt.pix_mp.height = mode->height;
|
||||
fmt.fmt.pix_mp.pixelformat = pixfmt;
|
||||
fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
|
||||
} else {
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
fmt.fmt.pix.width = mode->width;
|
||||
fmt.fmt.pix.height = mode->height;
|
||||
fmt.fmt.pix.pixelformat = pixfmt;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
||||
}
|
||||
|
||||
if (xioctl(camera->video_fd, request, &fmt) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (camera->use_mplane) {
|
||||
mode->width = fmt.fmt.pix_mp.width;
|
||||
mode->height = fmt.fmt.pix_mp.height;
|
||||
mode->pixel_format = mp_pixel_format_from_v4l_pixel_format(
|
||||
fmt.fmt.pix_mp.pixelformat);
|
||||
} else {
|
||||
mode->width = fmt.fmt.pix.width;
|
||||
mode->height = fmt.fmt.pix.height;
|
||||
mode->pixel_format = mp_pixel_format_from_v4l_pixel_format(
|
||||
fmt.fmt.pix.pixelformat);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
mp_camera_try_mode(MPCamera *camera, MPMode *mode)
|
||||
{
|
||||
if (!camera_mode_impl(camera, VIDIOC_TRY_FMT, mode)) {
|
||||
errno_printerr("VIDIOC_S_FMT");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const MPMode *
|
||||
mp_camera_get_mode(const MPCamera *camera)
|
||||
{
|
||||
return &camera->current_mode;
|
||||
}
|
||||
|
||||
bool
|
||||
mp_camera_set_mode(MPCamera *camera, MPMode *mode)
|
||||
{
|
||||
// Set the mode in the subdev the camera is one
|
||||
if (mp_camera_is_subdev(camera)) {
|
||||
struct v4l2_subdev_frame_interval interval = {};
|
||||
interval.pad = 0;
|
||||
interval.interval = mode->frame_interval;
|
||||
if (xioctl(camera->subdev_fd,
|
||||
VIDIOC_SUBDEV_S_FRAME_INTERVAL,
|
||||
&interval) == -1) {
|
||||
errno_printerr("VIDIOC_SUBDEV_S_FRAME_INTERVAL");
|
||||
}
|
||||
|
||||
bool did_set_frame_rate = interval.interval.numerator ==
|
||||
mode->frame_interval.numerator &&
|
||||
interval.interval.denominator ==
|
||||
mode->frame_interval.denominator;
|
||||
|
||||
struct v4l2_subdev_format fmt = {};
|
||||
fmt.pad = 0;
|
||||
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
||||
fmt.format.width = mode->width;
|
||||
fmt.format.height = mode->height;
|
||||
fmt.format.code =
|
||||
mp_pixel_format_to_v4l_bus_code(mode->pixel_format);
|
||||
fmt.format.field = V4L2_FIELD_ANY;
|
||||
if (xioctl(camera->subdev_fd, VIDIOC_SUBDEV_S_FMT, &fmt) == -1) {
|
||||
errno_printerr("VIDIOC_SUBDEV_S_FMT");
|
||||
return false;
|
||||
}
|
||||
|
||||
// sun6i-csi-bridge will return EINVAL when trying to read
|
||||
// frames if the resolution it's configured with doesn't match
|
||||
// the resolution of its input.
|
||||
if (camera->bridge_fd > 0) {
|
||||
struct v4l2_subdev_format bridge_fmt = fmt;
|
||||
|
||||
if (xioctl(camera->bridge_fd,
|
||||
VIDIOC_SUBDEV_S_FMT,
|
||||
&bridge_fmt) == -1) {
|
||||
errno_printerr("VIDIOC_SUBDEV_S_FMT");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fmt.format.width != bridge_fmt.format.width ||
|
||||
fmt.format.height != bridge_fmt.format.height) {
|
||||
g_printerr("Bridge format resolution mismatch\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Some drivers like ov5640 don't allow you to set the frame format
|
||||
// with too high a frame-rate, but that means the frame-rate won't be
|
||||
// set after the format change. So we need to try again here if we
|
||||
// didn't succeed before. Ideally we'd be able to set both at once.
|
||||
if (!did_set_frame_rate) {
|
||||
interval.interval = mode->frame_interval;
|
||||
if (xioctl(camera->subdev_fd,
|
||||
VIDIOC_SUBDEV_S_FRAME_INTERVAL,
|
||||
&interval) == -1) {
|
||||
errno_printerr("VIDIOC_SUBDEV_S_FRAME_INTERVAL");
|
||||
}
|
||||
}
|
||||
|
||||
// Update the mode
|
||||
mode->pixel_format =
|
||||
mp_pixel_format_from_v4l_bus_code(fmt.format.code);
|
||||
mode->frame_interval = interval.interval;
|
||||
mode->width = fmt.format.width;
|
||||
mode->height = fmt.format.height;
|
||||
}
|
||||
|
||||
// Set the mode for the video device
|
||||
{
|
||||
if (!camera_mode_impl(camera, VIDIOC_S_FMT, mode)) {
|
||||
errno_printerr("VIDIOC_S_FMT");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
camera->has_set_mode = true;
|
||||
camera->current_mode = *mode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
mp_camera_start_capture(MPCamera *camera)
|
||||
{
|
||||
g_return_val_if_fail(camera->has_set_mode, false);
|
||||
g_return_val_if_fail(camera->num_buffers == 0, false);
|
||||
|
||||
const enum v4l2_buf_type buftype = get_buf_type(camera);
|
||||
@@ -362,7 +182,7 @@ mp_camera_start_capture(MPCamera *camera)
|
||||
req.type = buftype;
|
||||
req.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (xioctl(camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
|
||||
if (xioctl(camera->camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
|
||||
errno_printerr("VIDIOC_REQBUFS");
|
||||
return false;
|
||||
}
|
||||
@@ -388,7 +208,7 @@ mp_camera_start_capture(MPCamera *camera)
|
||||
buf.length = 1;
|
||||
}
|
||||
|
||||
if (xioctl(camera->video_fd, VIDIOC_QUERYBUF, &buf) == -1) {
|
||||
if (xioctl(camera->camera->video_fd, VIDIOC_QUERYBUF, &buf) == -1) {
|
||||
errno_printerr("VIDIOC_QUERYBUF");
|
||||
break;
|
||||
}
|
||||
@@ -399,7 +219,7 @@ mp_camera_start_capture(MPCamera *camera)
|
||||
planes[0].length,
|
||||
PROT_READ,
|
||||
MAP_SHARED,
|
||||
camera->video_fd,
|
||||
camera->camera->video_fd,
|
||||
planes[0].m.mem_offset);
|
||||
} else {
|
||||
camera->buffers[i].length = buf.length;
|
||||
@@ -407,7 +227,7 @@ mp_camera_start_capture(MPCamera *camera)
|
||||
buf.length,
|
||||
PROT_READ,
|
||||
MAP_SHARED,
|
||||
camera->video_fd,
|
||||
camera->camera->video_fd,
|
||||
buf.m.offset);
|
||||
}
|
||||
|
||||
@@ -420,7 +240,7 @@ mp_camera_start_capture(MPCamera *camera)
|
||||
.type = buftype,
|
||||
.index = i,
|
||||
};
|
||||
if (xioctl(camera->video_fd, VIDIOC_EXPBUF, &expbuf) == -1) {
|
||||
if (xioctl(camera->camera->video_fd, VIDIOC_EXPBUF, &expbuf) == -1) {
|
||||
errno_printerr("VIDIOC_EXPBUF");
|
||||
break;
|
||||
}
|
||||
@@ -449,7 +269,7 @@ mp_camera_start_capture(MPCamera *camera)
|
||||
}
|
||||
|
||||
// Queue the buffer for capture
|
||||
if (xioctl(camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
|
||||
if (xioctl(camera->camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
|
||||
errno_printerr("VIDIOC_QBUF");
|
||||
goto error;
|
||||
}
|
||||
@@ -457,7 +277,7 @@ mp_camera_start_capture(MPCamera *camera)
|
||||
|
||||
// Start capture
|
||||
enum v4l2_buf_type type = buftype;
|
||||
if (xioctl(camera->video_fd, VIDIOC_STREAMON, &type) == -1) {
|
||||
if (xioctl(camera->camera->video_fd, VIDIOC_STREAMON, &type) == -1) {
|
||||
errno_printerr("VIDIOC_STREAMON");
|
||||
goto error;
|
||||
}
|
||||
@@ -485,7 +305,7 @@ error:
|
||||
req.type = buftype;
|
||||
req.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (xioctl(camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
|
||||
if (xioctl(camera->camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
|
||||
errno_printerr("VIDIOC_REQBUFS");
|
||||
}
|
||||
}
|
||||
@@ -501,7 +321,7 @@ mp_camera_stop_capture(MPCamera *camera)
|
||||
const enum v4l2_buf_type buftype = get_buf_type(camera);
|
||||
|
||||
enum v4l2_buf_type type = buftype;
|
||||
if (xioctl(camera->video_fd, VIDIOC_STREAMOFF, &type) == -1) {
|
||||
if (xioctl(camera->camera->video_fd, VIDIOC_STREAMOFF, &type) == -1) {
|
||||
errno_printerr("VIDIOC_STREAMOFF");
|
||||
}
|
||||
|
||||
@@ -523,7 +343,7 @@ mp_camera_stop_capture(MPCamera *camera)
|
||||
req.count = 0;
|
||||
req.type = buftype;
|
||||
req.memory = V4L2_MEMORY_MMAP;
|
||||
if (xioctl(camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
|
||||
if (xioctl(camera->camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
|
||||
errno_printerr("VIDIOC_REQBUFS");
|
||||
}
|
||||
|
||||
@@ -551,7 +371,7 @@ mp_camera_capture_buffer(MPCamera *camera, MPBuffer *buffer)
|
||||
buf.length = 1;
|
||||
}
|
||||
|
||||
if (xioctl(camera->video_fd, VIDIOC_DQBUF, &buf) == -1) {
|
||||
if (xioctl(camera->camera->video_fd, VIDIOC_DQBUF, &buf) == -1) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
return true;
|
||||
@@ -565,9 +385,9 @@ mp_camera_capture_buffer(MPCamera *camera, MPBuffer *buffer)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t pixel_format = camera->current_mode.pixel_format;
|
||||
uint32_t width = camera->current_mode.width;
|
||||
uint32_t height = camera->current_mode.height;
|
||||
int format = camera->camera->current_mode->format;
|
||||
uint32_t width = camera->camera->current_mode->width;
|
||||
uint32_t height = camera->camera->current_mode->height;
|
||||
|
||||
uint32_t bytesused;
|
||||
if (camera->use_mplane) {
|
||||
@@ -576,8 +396,8 @@ mp_camera_capture_buffer(MPCamera *camera, MPBuffer *buffer)
|
||||
bytesused = buf.bytesused;
|
||||
}
|
||||
|
||||
assert(bytesused == (mp_pixel_format_width_to_bytes(pixel_format, width) +
|
||||
mp_pixel_format_width_to_padding(pixel_format, width)) *
|
||||
assert(bytesused == (libmegapixels_mode_width_to_bytes(format, width) +
|
||||
libmegapixels_mode_width_to_padding(format, width)) *
|
||||
height);
|
||||
assert(bytesused == camera->buffers[buf.index].length);
|
||||
|
||||
@@ -604,243 +424,13 @@ mp_camera_release_buffer(MPCamera *camera, uint32_t buffer_index)
|
||||
buf.length = 1;
|
||||
}
|
||||
|
||||
if (xioctl(camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
|
||||
if (xioctl(camera->camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
|
||||
errno_printerr("VIDIOC_QBUF");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static MPModeList *
|
||||
get_subdev_modes(MPCamera *camera, bool (*check)(MPCamera *, MPMode *))
|
||||
{
|
||||
MPModeList *item = NULL;
|
||||
|
||||
for (uint32_t fmt_index = 0;; ++fmt_index) {
|
||||
struct v4l2_subdev_mbus_code_enum fmt = {};
|
||||
fmt.index = fmt_index;
|
||||
fmt.pad = 0;
|
||||
fmt.which = V4L2_SUBDEV_FORMAT_TRY;
|
||||
if (xioctl(camera->subdev_fd, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &fmt) ==
|
||||
-1) {
|
||||
if (errno != EINVAL) {
|
||||
errno_printerr("VIDIOC_SUBDEV_ENUM_MBUS_CODE");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip unsupported formats
|
||||
uint32_t format = mp_pixel_format_from_v4l_bus_code(fmt.code);
|
||||
if (format == MP_PIXEL_FMT_UNSUPPORTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t frame_index = 0;; ++frame_index) {
|
||||
struct v4l2_subdev_frame_size_enum frame = {};
|
||||
frame.index = frame_index;
|
||||
frame.pad = 0;
|
||||
frame.code = fmt.code;
|
||||
frame.which = V4L2_SUBDEV_FORMAT_TRY;
|
||||
if (xioctl(camera->subdev_fd,
|
||||
VIDIOC_SUBDEV_ENUM_FRAME_SIZE,
|
||||
&frame) == -1) {
|
||||
if (errno != EINVAL) {
|
||||
errno_printerr(
|
||||
"VIDIOC_SUBDEV_ENUM_FRAME_SIZE");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Handle other types
|
||||
if (frame.min_width != frame.max_width ||
|
||||
frame.min_height != frame.max_height) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint32_t interval_index = 0;; ++interval_index) {
|
||||
struct v4l2_subdev_frame_interval_enum interval = {};
|
||||
interval.index = interval_index;
|
||||
interval.pad = 0;
|
||||
interval.code = fmt.code;
|
||||
interval.width = frame.max_width;
|
||||
interval.height = frame.max_height;
|
||||
interval.which = V4L2_SUBDEV_FORMAT_TRY;
|
||||
if (xioctl(camera->subdev_fd,
|
||||
VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL,
|
||||
&interval) == -1) {
|
||||
if (errno != EINVAL) {
|
||||
errno_printerr(
|
||||
"VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
MPMode mode = {
|
||||
.pixel_format = format,
|
||||
.frame_interval = interval.interval,
|
||||
.width = frame.max_width,
|
||||
.height = frame.max_height,
|
||||
};
|
||||
|
||||
if (!check(camera, &mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MPModeList *new_item = malloc(sizeof(MPModeList));
|
||||
new_item->mode = mode;
|
||||
new_item->next = item;
|
||||
item = new_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static MPModeList *
|
||||
get_video_modes(MPCamera *camera, bool (*check)(MPCamera *, MPMode *))
|
||||
{
|
||||
const enum v4l2_buf_type buftype = get_buf_type(camera);
|
||||
|
||||
MPModeList *item = NULL;
|
||||
|
||||
for (uint32_t fmt_index = 0;; ++fmt_index) {
|
||||
struct v4l2_fmtdesc fmt = {};
|
||||
fmt.index = fmt_index;
|
||||
fmt.type = buftype;
|
||||
if (xioctl(camera->video_fd, VIDIOC_ENUM_FMT, &fmt) == -1) {
|
||||
if (errno != EINVAL) {
|
||||
errno_printerr("VIDIOC_ENUM_FMT");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip unsupported formats
|
||||
uint32_t format =
|
||||
mp_pixel_format_from_v4l_pixel_format(fmt.pixelformat);
|
||||
if (format == MP_PIXEL_FMT_UNSUPPORTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t frame_index = 0;; ++frame_index) {
|
||||
struct v4l2_frmsizeenum frame = {};
|
||||
frame.index = frame_index;
|
||||
frame.pixel_format = fmt.pixelformat;
|
||||
if (xioctl(camera->video_fd,
|
||||
VIDIOC_ENUM_FRAMESIZES,
|
||||
&frame) == -1) {
|
||||
if (errno != EINVAL) {
|
||||
errno_printerr("VIDIOC_ENUM_FRAMESIZES");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Handle other types
|
||||
if (frame.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint32_t interval_index = 0;; ++interval_index) {
|
||||
struct v4l2_frmivalenum interval = {};
|
||||
interval.index = interval_index;
|
||||
interval.pixel_format = fmt.pixelformat;
|
||||
interval.width = frame.discrete.width;
|
||||
interval.height = frame.discrete.height;
|
||||
if (xioctl(camera->video_fd,
|
||||
VIDIOC_ENUM_FRAMEINTERVALS,
|
||||
&interval) == -1) {
|
||||
if (errno != EINVAL) {
|
||||
errno_printerr(
|
||||
"VIDIOC_ENUM_FRAMESIZES");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Handle other types
|
||||
if (interval.type != V4L2_FRMIVAL_TYPE_DISCRETE) {
|
||||
break;
|
||||
}
|
||||
|
||||
MPMode mode = {
|
||||
.pixel_format = format,
|
||||
.frame_interval = interval.discrete,
|
||||
.width = frame.discrete.width,
|
||||
.height = frame.discrete.height,
|
||||
};
|
||||
|
||||
if (!check(camera, &mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MPModeList *new_item = malloc(sizeof(MPModeList));
|
||||
new_item->mode = mode;
|
||||
new_item->next = item;
|
||||
item = new_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static bool
|
||||
all_modes(MPCamera *camera, MPMode *mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
available_modes(MPCamera *camera, MPMode *mode)
|
||||
{
|
||||
MPMode attempt = *mode;
|
||||
return mp_camera_try_mode(camera, &attempt) &&
|
||||
mp_mode_is_equivalent(mode, &attempt);
|
||||
}
|
||||
|
||||
MPModeList *
|
||||
mp_camera_list_supported_modes(MPCamera *camera)
|
||||
{
|
||||
if (mp_camera_is_subdev(camera)) {
|
||||
return get_subdev_modes(camera, all_modes);
|
||||
} else {
|
||||
return get_video_modes(camera, all_modes);
|
||||
}
|
||||
}
|
||||
|
||||
MPModeList *
|
||||
mp_camera_list_available_modes(MPCamera *camera)
|
||||
{
|
||||
if (mp_camera_is_subdev(camera)) {
|
||||
return get_subdev_modes(camera, available_modes);
|
||||
} else {
|
||||
return get_video_modes(camera, available_modes);
|
||||
}
|
||||
}
|
||||
|
||||
MPMode *
|
||||
mp_camera_mode_list_get(MPModeList *list)
|
||||
{
|
||||
g_return_val_if_fail(list, NULL);
|
||||
return &list->mode;
|
||||
}
|
||||
|
||||
MPModeList *
|
||||
mp_camera_mode_list_next(MPModeList *list)
|
||||
{
|
||||
g_return_val_if_fail(list, NULL);
|
||||
return list->next;
|
||||
}
|
||||
|
||||
void
|
||||
mp_camera_mode_list_free(MPModeList *list)
|
||||
{
|
||||
while (list) {
|
||||
MPModeList *tmp = list;
|
||||
list = tmp->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
struct int_str_pair {
|
||||
uint32_t value;
|
||||
const char *str;
|
||||
@@ -1010,24 +600,15 @@ struct _MPControlList {
|
||||
MPControlList *next;
|
||||
};
|
||||
|
||||
static int
|
||||
control_fd(MPCamera *camera)
|
||||
{
|
||||
if (camera->subdev_fd != -1) {
|
||||
return camera->subdev_fd;
|
||||
}
|
||||
return camera->video_fd;
|
||||
}
|
||||
|
||||
MPControlList *
|
||||
mp_camera_list_controls(MPCamera *camera)
|
||||
mp_camera_list_controls(libmegapixels_camera *camera)
|
||||
{
|
||||
MPControlList *item = NULL;
|
||||
|
||||
struct v4l2_query_ext_ctrl ctrl = {};
|
||||
ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
|
||||
while (true) {
|
||||
if (xioctl(control_fd(camera), VIDIOC_QUERY_EXT_CTRL, &ctrl) == -1) {
|
||||
if (xioctl(camera->sensor_fd, VIDIOC_QUERY_EXT_CTRL, &ctrl) == -1) {
|
||||
if (errno != EINVAL) {
|
||||
errno_printerr("VIDIOC_QUERY_EXT_CTRL");
|
||||
}
|
||||
@@ -1090,11 +671,13 @@ mp_control_list_free(MPControlList *list)
|
||||
}
|
||||
|
||||
bool
|
||||
mp_camera_query_control(MPCamera *camera, uint32_t id, MPControl *control)
|
||||
mp_camera_query_control(libmegapixels_camera *camera,
|
||||
uint32_t id,
|
||||
MPControl *control)
|
||||
{
|
||||
struct v4l2_query_ext_ctrl ctrl = {};
|
||||
ctrl.id = id;
|
||||
if (xioctl(control_fd(camera), VIDIOC_QUERY_EXT_CTRL, &ctrl) == -1) {
|
||||
if (xioctl(camera->sensor_fd, VIDIOC_QUERY_EXT_CTRL, &ctrl) == -1) {
|
||||
if (errno != EINVAL) {
|
||||
errno_printerr("VIDIOC_QUERY_EXT_CTRL");
|
||||
}
|
||||
@@ -1121,7 +704,10 @@ mp_camera_query_control(MPCamera *camera, uint32_t id, MPControl *control)
|
||||
}
|
||||
|
||||
static bool
|
||||
control_impl_int32(MPCamera *camera, uint32_t id, int request, int32_t *value)
|
||||
control_impl_int32(libmegapixels_camera *camera,
|
||||
uint32_t id,
|
||||
int request,
|
||||
int32_t *value)
|
||||
{
|
||||
struct v4l2_ext_control ctrl = {};
|
||||
ctrl.id = id;
|
||||
@@ -1133,7 +719,7 @@ control_impl_int32(MPCamera *camera, uint32_t id, int request, int32_t *value)
|
||||
.count = 1,
|
||||
.controls = &ctrl,
|
||||
};
|
||||
if (xioctl(control_fd(camera), request, &ctrls) == -1) {
|
||||
if (xioctl(camera->sensor_fd, request, &ctrls) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1142,7 +728,7 @@ control_impl_int32(MPCamera *camera, uint32_t id, int request, int32_t *value)
|
||||
}
|
||||
|
||||
pid_t
|
||||
mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v)
|
||||
mp_camera_control_set_int32_bg(libmegapixels_camera *camera, uint32_t id, int32_t v)
|
||||
{
|
||||
struct v4l2_ext_control ctrl = {};
|
||||
ctrl.id = id;
|
||||
@@ -1155,7 +741,7 @@ mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v)
|
||||
.controls = &ctrl,
|
||||
};
|
||||
|
||||
int fd = control_fd(camera);
|
||||
int fd = camera->sensor_fd;
|
||||
|
||||
// fork only after all the memory has been read
|
||||
pid_t pid = fork();
|
||||
@@ -1174,19 +760,19 @@ mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v)
|
||||
}
|
||||
|
||||
bool
|
||||
mp_camera_control_try_int32(MPCamera *camera, uint32_t id, int32_t *v)
|
||||
mp_camera_control_try_int32(libmegapixels_camera *camera, uint32_t id, int32_t *v)
|
||||
{
|
||||
return control_impl_int32(camera, id, VIDIOC_TRY_EXT_CTRLS, v);
|
||||
}
|
||||
|
||||
bool
|
||||
mp_camera_control_set_int32(MPCamera *camera, uint32_t id, int32_t v)
|
||||
mp_camera_control_set_int32(libmegapixels_camera *camera, uint32_t id, int32_t v)
|
||||
{
|
||||
return control_impl_int32(camera, id, VIDIOC_S_EXT_CTRLS, &v);
|
||||
}
|
||||
|
||||
int32_t
|
||||
mp_camera_control_get_int32(MPCamera *camera, uint32_t id)
|
||||
mp_camera_control_get_int32(libmegapixels_camera *camera, uint32_t id)
|
||||
{
|
||||
int32_t v = 0;
|
||||
control_impl_int32(camera, id, VIDIOC_G_EXT_CTRLS, &v);
|
||||
@@ -1194,7 +780,7 @@ mp_camera_control_get_int32(MPCamera *camera, uint32_t id)
|
||||
}
|
||||
|
||||
bool
|
||||
mp_camera_control_try_boolean(MPCamera *camera, uint32_t id, bool *v)
|
||||
mp_camera_control_try_boolean(libmegapixels_camera *camera, uint32_t id, bool *v)
|
||||
{
|
||||
int32_t value = *v;
|
||||
bool s = control_impl_int32(camera, id, VIDIOC_TRY_EXT_CTRLS, &value);
|
||||
@@ -1203,14 +789,14 @@ mp_camera_control_try_boolean(MPCamera *camera, uint32_t id, bool *v)
|
||||
}
|
||||
|
||||
bool
|
||||
mp_camera_control_set_bool(MPCamera *camera, uint32_t id, bool v)
|
||||
mp_camera_control_set_bool(libmegapixels_camera *camera, uint32_t id, bool v)
|
||||
{
|
||||
int32_t value = v;
|
||||
return control_impl_int32(camera, id, VIDIOC_S_EXT_CTRLS, &value);
|
||||
}
|
||||
|
||||
bool
|
||||
mp_camera_control_get_bool(MPCamera *camera, uint32_t id)
|
||||
mp_camera_control_get_bool(libmegapixels_camera *camera, uint32_t id)
|
||||
{
|
||||
int32_t v = false;
|
||||
control_impl_int32(camera, id, VIDIOC_G_EXT_CTRLS, &v);
|
||||
@@ -1218,7 +804,7 @@ mp_camera_control_get_bool(MPCamera *camera, uint32_t id)
|
||||
}
|
||||
|
||||
pid_t
|
||||
mp_camera_control_set_bool_bg(MPCamera *camera, uint32_t id, bool v)
|
||||
mp_camera_control_set_bool_bg(libmegapixels_camera *camera, uint32_t id, bool v)
|
||||
{
|
||||
int32_t value = v;
|
||||
return mp_camera_control_set_int32_bg(camera, id, value);
|
||||
|
42
src/camera.h
42
src/camera.h
@@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "mode.h"
|
||||
|
||||
#include <libmegapixels.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define MAX_VIDEO_BUFFERS 20
|
||||
#define MAX_BG_TASKS 8
|
||||
|
||||
typedef struct {
|
||||
uint32_t index;
|
||||
|
||||
@@ -15,33 +18,20 @@ typedef struct {
|
||||
|
||||
typedef struct _MPCamera MPCamera;
|
||||
|
||||
MPCamera *mp_camera_new(int video_fd, int subdev_fd, int bridge_fd);
|
||||
void mp_camera_free(MPCamera *camera);
|
||||
MPCamera *mp_camera_new(libmegapixels_camera *camera);
|
||||
|
||||
void mp_camera_add_bg_task(MPCamera *camera, pid_t pid);
|
||||
void mp_camera_wait_bg_tasks(MPCamera *camera);
|
||||
bool mp_camera_check_task_complete(MPCamera *camera, pid_t pid);
|
||||
|
||||
bool mp_camera_is_subdev(MPCamera *camera);
|
||||
int mp_camera_get_video_fd(MPCamera *camera);
|
||||
int mp_camera_get_subdev_fd(MPCamera *camera);
|
||||
|
||||
const MPMode *mp_camera_get_mode(const MPCamera *camera);
|
||||
bool mp_camera_try_mode(MPCamera *camera, MPMode *mode);
|
||||
|
||||
bool mp_camera_set_mode(MPCamera *camera, MPMode *mode);
|
||||
bool mp_camera_start_capture(MPCamera *camera);
|
||||
bool mp_camera_stop_capture(MPCamera *camera);
|
||||
bool mp_camera_is_capturing(MPCamera *camera);
|
||||
bool mp_camera_capture_buffer(MPCamera *camera, MPBuffer *buffer);
|
||||
bool mp_camera_release_buffer(MPCamera *camera, uint32_t buffer_index);
|
||||
|
||||
MPModeList *mp_camera_list_supported_modes(MPCamera *camera);
|
||||
MPModeList *mp_camera_list_available_modes(MPCamera *camera);
|
||||
MPMode *mp_camera_mode_list_get(MPModeList *list);
|
||||
MPModeList *mp_camera_mode_list_next(MPModeList *list);
|
||||
void mp_camera_mode_list_free(MPModeList *list);
|
||||
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint32_t type;
|
||||
@@ -65,21 +55,21 @@ const char *mp_control_type_to_str(uint32_t type);
|
||||
|
||||
typedef struct _MPControlList MPControlList;
|
||||
|
||||
MPControlList *mp_camera_list_controls(MPCamera *camera);
|
||||
MPControlList *mp_camera_list_controls(libmegapixels_camera *camera);
|
||||
MPControl *mp_control_list_get(MPControlList *list);
|
||||
MPControlList *mp_control_list_next(MPControlList *list);
|
||||
void mp_control_list_free(MPControlList *list);
|
||||
|
||||
bool mp_camera_query_control(MPCamera *camera, uint32_t id, MPControl *control);
|
||||
bool mp_camera_query_control(libmegapixels_camera *camera, uint32_t id, MPControl *control);
|
||||
|
||||
bool mp_camera_control_try_int32(MPCamera *camera, uint32_t id, int32_t *v);
|
||||
bool mp_camera_control_set_int32(MPCamera *camera, uint32_t id, int32_t v);
|
||||
int32_t mp_camera_control_get_int32(MPCamera *camera, uint32_t id);
|
||||
bool mp_camera_control_try_int32(libmegapixels_camera *camera, uint32_t id, int32_t *v);
|
||||
bool mp_camera_control_set_int32(libmegapixels_camera *camera, uint32_t id, int32_t v);
|
||||
int32_t mp_camera_control_get_int32(libmegapixels_camera *camera, uint32_t id);
|
||||
// set the value in the background, discards result
|
||||
pid_t mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v);
|
||||
pid_t mp_camera_control_set_int32_bg(libmegapixels_camera *camera, uint32_t id, int32_t v);
|
||||
|
||||
bool mp_camera_control_try_bool(MPCamera *camera, uint32_t id, bool *v);
|
||||
bool mp_camera_control_set_bool(MPCamera *camera, uint32_t id, bool v);
|
||||
bool mp_camera_control_get_bool(MPCamera *camera, uint32_t id);
|
||||
bool mp_camera_control_try_bool(libmegapixels_camera *camera, uint32_t id, bool *v);
|
||||
bool mp_camera_control_set_bool(libmegapixels_camera *camera, uint32_t id, bool v);
|
||||
bool mp_camera_control_get_bool(libmegapixels_camera *camera, uint32_t id);
|
||||
// set the value in the background, discards result
|
||||
pid_t mp_camera_control_set_bool_bg(MPCamera *camera, uint32_t id, bool v);
|
||||
pid_t mp_camera_control_set_bool_bg(libmegapixels_camera *camera, uint32_t id, bool v);
|
||||
|
@@ -1,379 +0,0 @@
|
||||
#include "camera_config.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "ini.h"
|
||||
#include "matrix.h"
|
||||
#include <assert.h>
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static struct mp_camera_config cameras[MP_MAX_CAMERAS];
|
||||
static size_t num_cameras = 0;
|
||||
|
||||
static char *exif_make;
|
||||
static char *exif_model;
|
||||
|
||||
static bool
|
||||
find_config(char *conffile)
|
||||
{
|
||||
char buf[512];
|
||||
FILE *fp;
|
||||
|
||||
if (access("/proc/device-tree/compatible", F_OK) != -1) {
|
||||
// Reads to compatible string of the current device tree, looks like:
|
||||
// pine64,pinephone-1.2\0allwinner,sun50i-a64\0
|
||||
fp = fopen("/proc/device-tree/compatible", "r");
|
||||
fgets(buf, 512, fp);
|
||||
fclose(fp);
|
||||
|
||||
// Check config/%dt.ini in the current working directory
|
||||
sprintf(conffile, "config/%s.ini", buf);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
printf("Found config file at %s\n", conffile);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for a config file in XDG_CONFIG_HOME
|
||||
sprintf(conffile,
|
||||
"%s/megapixels/config/%s.ini",
|
||||
g_get_user_config_dir(),
|
||||
buf);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
printf("Found config file at %s\n", conffile);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check user overridden /etc/megapixels/config/$dt.ini
|
||||
sprintf(conffile, "%s/megapixels/config/%s.ini", SYSCONFDIR, buf);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
printf("Found config file at %s\n", conffile);
|
||||
return true;
|
||||
}
|
||||
// Check packaged /usr/share/megapixels/config/$dt.ini
|
||||
sprintf(conffile, "%s/megapixels/config/%s.ini", DATADIR, buf);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
printf("Found config file at %s\n", conffile);
|
||||
return true;
|
||||
}
|
||||
printf("%s not found\n", conffile);
|
||||
} else {
|
||||
printf("Could not read device name from device tree\n");
|
||||
}
|
||||
|
||||
// If all else fails, fall back to /etc/megapixels.ini
|
||||
sprintf(conffile, "/etc/megapixels.ini");
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
printf("Found config file at %s\n", conffile);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
find_calibration_by_model(char *conffile, char *model, const char *sensor)
|
||||
{
|
||||
// Check config/%model,%sensor.dcp in the current working directory
|
||||
sprintf(conffile, "config/%s,%s.dcp", model, sensor);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
printf("Found calibration file at %s\n", conffile);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for a calibration file in XDG_CONFIG_HOME
|
||||
sprintf(conffile,
|
||||
"%s/megapixels/config/%s,%s.dcp",
|
||||
g_get_user_config_dir(),
|
||||
model,
|
||||
sensor);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
printf("Found calibration file at %s\n", conffile);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check user overridden /etc/megapixels/config/%model,%sensor.dcp
|
||||
sprintf(conffile,
|
||||
"%s/megapixels/config/%s,%s.dcp",
|
||||
SYSCONFDIR,
|
||||
model,
|
||||
sensor);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
printf("Found calibration file at %s\n", conffile);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check packaged /usr/share/megapixels/config/%model,%sensor.ini
|
||||
sprintf(conffile, "%s/megapixels/config/%s,%s.dcp", DATADIR, model, sensor);
|
||||
if (access(conffile, F_OK) != -1) {
|
||||
printf("Found calibration file at %s\n", conffile);
|
||||
return true;
|
||||
}
|
||||
printf("No calibration found for %s,%s\n", model, sensor);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
find_calibration(char *conffile, const char *sensor)
|
||||
{
|
||||
char model[512];
|
||||
FILE *fp;
|
||||
|
||||
if (access("/proc/device-tree/compatible", F_OK) == -1) {
|
||||
return false;
|
||||
}
|
||||
fp = fopen("/proc/device-tree/compatible", "r");
|
||||
char *modelptr = model;
|
||||
while (1) {
|
||||
int c = fgetc(fp);
|
||||
if (c == EOF) {
|
||||
*(modelptr) = '\0';
|
||||
return find_calibration_by_model(conffile, model, sensor);
|
||||
}
|
||||
*(modelptr++) = (char)c;
|
||||
if (c == 0) {
|
||||
bool res =
|
||||
find_calibration_by_model(conffile, model, sensor);
|
||||
if (res) {
|
||||
return true;
|
||||
}
|
||||
modelptr = model;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
strtoint(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
long x = strtol(nptr, endptr, base);
|
||||
assert(x <= INT_MAX);
|
||||
return (int)x;
|
||||
}
|
||||
|
||||
static bool
|
||||
config_handle_camera_mode(const char *prefix,
|
||||
MPMode *mode,
|
||||
const char *name,
|
||||
const char *value)
|
||||
{
|
||||
int prefix_length = strlen(prefix);
|
||||
if (strncmp(prefix, name, prefix_length) != 0)
|
||||
return false;
|
||||
|
||||
name += prefix_length;
|
||||
|
||||
if (strcmp(name, "width") == 0) {
|
||||
mode->width = strtoint(value, NULL, 10);
|
||||
} else if (strcmp(name, "height") == 0) {
|
||||
mode->height = strtoint(value, NULL, 10);
|
||||
} else if (strcmp(name, "rate") == 0) {
|
||||
mode->frame_interval.numerator = 1;
|
||||
mode->frame_interval.denominator = strtoint(value, NULL, 10);
|
||||
} else if (strcmp(name, "fmt") == 0) {
|
||||
mode->pixel_format = mp_pixel_format_from_str(value);
|
||||
if (mode->pixel_format == MP_PIXEL_FMT_UNSUPPORTED) {
|
||||
g_printerr("Unsupported pixelformat %s\n", value);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
config_ini_handler(void *user,
|
||||
const char *section,
|
||||
const char *name,
|
||||
const char *value)
|
||||
{
|
||||
if (strcmp(section, "device") == 0) {
|
||||
if (strcmp(name, "make") == 0) {
|
||||
exif_make = strdup(value);
|
||||
} else if (strcmp(name, "model") == 0) {
|
||||
exif_model = strdup(value);
|
||||
} else {
|
||||
g_printerr("Unknown key '%s' in [device]\n", name);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (num_cameras == MP_MAX_CAMERAS) {
|
||||
g_printerr("More cameras defined than NUM_CAMERAS\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
for (; index < num_cameras; ++index) {
|
||||
if (strcmp(cameras[index].cfg_name, section) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == num_cameras) {
|
||||
printf("Adding camera %s from config\n", section);
|
||||
++num_cameras;
|
||||
|
||||
cameras[index].index = index;
|
||||
strcpy(cameras[index].cfg_name, section);
|
||||
|
||||
char cal_path[512];
|
||||
if (find_calibration(cal_path, section)) {
|
||||
cameras[index].calibration =
|
||||
parse_calibration_file(cal_path);
|
||||
}
|
||||
}
|
||||
|
||||
struct mp_camera_config *cc = &cameras[index];
|
||||
|
||||
if (config_handle_camera_mode(
|
||||
"capture-", &cc->capture_mode, name, value)) {
|
||||
} else if (config_handle_camera_mode(
|
||||
"preview-", &cc->preview_mode, name, value)) {
|
||||
} else if (strcmp(name, "rotate") == 0) {
|
||||
cc->rotate = strtoint(value, NULL, 10);
|
||||
} else if (strcmp(name, "mirrored") == 0) {
|
||||
cc->mirrored = strcmp(value, "true") == 0;
|
||||
} else if (strcmp(name, "driver") == 0) {
|
||||
strcpy(cc->dev_name, value);
|
||||
} else if (strcmp(name, "media-driver") == 0) {
|
||||
strcpy(cc->media_dev_name, value);
|
||||
} else if (strcmp(name, "media-links") == 0) {
|
||||
char **linkdefs = g_strsplit(value, ",", 0);
|
||||
|
||||
for (int i = 0; i < MP_MAX_LINKS && linkdefs[i] != NULL;
|
||||
++i) {
|
||||
char **linkdef = g_strsplit(linkdefs[i], "->", 2);
|
||||
char **porta = g_strsplit(linkdef[0], ":", 2);
|
||||
char **portb = g_strsplit(linkdef[1], ":", 2);
|
||||
|
||||
strcpy(cc->media_links[i].source_name, porta[0]);
|
||||
strcpy(cc->media_links[i].target_name, portb[0]);
|
||||
cc->media_links[i].source_port =
|
||||
strtoint(porta[1], NULL, 10);
|
||||
cc->media_links[i].target_port =
|
||||
strtoint(portb[1], NULL, 10);
|
||||
|
||||
g_strfreev(portb);
|
||||
g_strfreev(porta);
|
||||
g_strfreev(linkdef);
|
||||
++cc->num_media_links;
|
||||
}
|
||||
g_strfreev(linkdefs);
|
||||
} else if (strcmp(name, "colormatrix") == 0) {
|
||||
sscanf(value,
|
||||
"%f,%f,%f,%f,%f,%f,%f,%f,%f",
|
||||
cc->colormatrix + 0,
|
||||
cc->colormatrix + 1,
|
||||
cc->colormatrix + 2,
|
||||
cc->colormatrix + 3,
|
||||
cc->colormatrix + 4,
|
||||
cc->colormatrix + 5,
|
||||
cc->colormatrix + 6,
|
||||
cc->colormatrix + 7,
|
||||
cc->colormatrix + 8);
|
||||
} else if (strcmp(name, "forwardmatrix") == 0) {
|
||||
sscanf(value,
|
||||
"%f,%f,%f,%f,%f,%f,%f,%f,%f",
|
||||
cc->forwardmatrix + 0,
|
||||
cc->forwardmatrix + 1,
|
||||
cc->forwardmatrix + 2,
|
||||
cc->forwardmatrix + 3,
|
||||
cc->forwardmatrix + 4,
|
||||
cc->forwardmatrix + 5,
|
||||
cc->forwardmatrix + 6,
|
||||
cc->forwardmatrix + 7,
|
||||
cc->forwardmatrix + 8);
|
||||
} else if (strcmp(name, "whitelevel") == 0) {
|
||||
cc->whitelevel = strtoint(value, NULL, 10);
|
||||
} else if (strcmp(name, "blacklevel") == 0) {
|
||||
cc->blacklevel = strtoint(value, NULL, 10);
|
||||
} else if (strcmp(name, "focallength") == 0) {
|
||||
cc->focallength = strtof(value, NULL);
|
||||
} else if (strcmp(name, "cropfactor") == 0) {
|
||||
cc->cropfactor = strtof(value, NULL);
|
||||
} else if (strcmp(name, "fnumber") == 0) {
|
||||
cc->fnumber = strtod(value, NULL);
|
||||
} else if (strcmp(name, "iso-min") == 0) {
|
||||
cc->iso_min = strtod(value, NULL);
|
||||
} else if (strcmp(name, "iso-max") == 0) {
|
||||
cc->iso_max = strtod(value, NULL);
|
||||
} else if (strcmp(name, "flash-path") == 0) {
|
||||
strcpy(cc->flash_path, value);
|
||||
cc->has_flash = true;
|
||||
} else if (strcmp(name, "flash-display") == 0) {
|
||||
cc->flash_display = strcmp(value, "true") == 0;
|
||||
|
||||
if (cc->flash_display) {
|
||||
cc->has_flash = true;
|
||||
}
|
||||
} else {
|
||||
g_printerr("Unknown key '%s' in [%s]\n", name, section);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
calculate_matrices()
|
||||
{
|
||||
for (size_t i = 0; i < MP_MAX_CAMERAS; ++i) {
|
||||
if (cameras[i].colormatrix != NULL &&
|
||||
cameras[i].forwardmatrix != NULL) {
|
||||
multiply_matrices(cameras[i].colormatrix,
|
||||
cameras[i].forwardmatrix,
|
||||
cameras[i].previewmatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
mp_load_config()
|
||||
{
|
||||
char file[512];
|
||||
if (!find_config(file)) {
|
||||
g_printerr("Could not find any config file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = ini_parse(file, config_ini_handler, NULL);
|
||||
if (result == -1) {
|
||||
g_printerr("Config file not found\n");
|
||||
return false;
|
||||
}
|
||||
if (result == -2) {
|
||||
g_printerr("Could not allocate memory to parse config file\n");
|
||||
return false;
|
||||
}
|
||||
if (result != 0) {
|
||||
g_printerr("Could not parse config file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
calculate_matrices();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *
|
||||
mp_get_device_make()
|
||||
{
|
||||
return exif_make;
|
||||
}
|
||||
|
||||
const char *
|
||||
mp_get_device_model()
|
||||
{
|
||||
return exif_model;
|
||||
}
|
||||
|
||||
const struct mp_camera_config *
|
||||
mp_get_camera_config(size_t index)
|
||||
{
|
||||
if (index >= num_cameras)
|
||||
return NULL;
|
||||
|
||||
return &cameras[index];
|
||||
}
|
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "dcp.h"
|
||||
#include "mode.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define MP_MAX_CAMERAS 5
|
||||
#define MP_MAX_LINKS 10
|
||||
|
||||
struct mp_media_link_config {
|
||||
char source_name[100];
|
||||
char target_name[100];
|
||||
int source_port;
|
||||
int target_port;
|
||||
};
|
||||
|
||||
struct mp_camera_config {
|
||||
size_t index;
|
||||
|
||||
char cfg_name[100];
|
||||
char dev_name[260];
|
||||
char media_dev_name[260];
|
||||
|
||||
MPMode capture_mode;
|
||||
MPMode preview_mode;
|
||||
int rotate;
|
||||
bool mirrored;
|
||||
|
||||
struct mp_media_link_config media_links[MP_MAX_LINKS];
|
||||
int num_media_links;
|
||||
|
||||
float colormatrix[9];
|
||||
float forwardmatrix[9];
|
||||
float previewmatrix[9];
|
||||
int blacklevel;
|
||||
int whitelevel;
|
||||
|
||||
struct MPCameraCalibration calibration;
|
||||
|
||||
float focallength;
|
||||
float cropfactor;
|
||||
double fnumber;
|
||||
int iso_min;
|
||||
int iso_max;
|
||||
|
||||
char flash_path[260];
|
||||
bool flash_display;
|
||||
bool has_flash;
|
||||
};
|
||||
|
||||
bool mp_load_config();
|
||||
|
||||
const char *mp_get_device_make();
|
||||
const char *mp_get_device_model();
|
||||
const struct mp_camera_config *mp_get_camera_config(size_t index);
|
536
src/device.c
536
src/device.c
@@ -1,536 +0,0 @@
|
||||
#include "device.h"
|
||||
#include "mode.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool
|
||||
mp_find_device_path(struct media_v2_intf_devnode devnode, char *path, int length)
|
||||
{
|
||||
char uevent_path[256];
|
||||
snprintf(uevent_path,
|
||||
256,
|
||||
"/sys/dev/char/%d:%d/uevent",
|
||||
devnode.major,
|
||||
devnode.minor);
|
||||
|
||||
FILE *f = fopen(uevent_path, "r");
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char line[512];
|
||||
while (fgets(line, 512, f)) {
|
||||
if (strncmp(line, "DEVNAME=", 8) == 0) {
|
||||
// Drop newline
|
||||
int length = strlen(line);
|
||||
if (line[length - 1] == '\n')
|
||||
line[length - 1] = '\0';
|
||||
|
||||
snprintf(path, length, "/dev/%s", line + 8);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct _MPDevice {
|
||||
int fd;
|
||||
|
||||
struct media_device_info info;
|
||||
|
||||
struct media_v2_entity *entities;
|
||||
size_t num_entities;
|
||||
struct media_v2_interface *interfaces;
|
||||
size_t num_interfaces;
|
||||
struct media_v2_pad *pads;
|
||||
size_t num_pads;
|
||||
struct media_v2_link *links;
|
||||
size_t num_links;
|
||||
};
|
||||
|
||||
static void
|
||||
errno_printerr(const char *s)
|
||||
{
|
||||
g_printerr("MPDevice: %s error %d, %s\n", s, errno, strerror(errno));
|
||||
}
|
||||
|
||||
static int
|
||||
xioctl(int fd, int request, void *arg)
|
||||
{
|
||||
int r;
|
||||
do {
|
||||
r = ioctl(fd, request, arg);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
return r;
|
||||
}
|
||||
|
||||
MPDevice *
|
||||
mp_device_find(const char *driver_name, const char *dev_name)
|
||||
{
|
||||
MPDeviceList *list = mp_device_list_new();
|
||||
|
||||
MPDevice *found_device =
|
||||
mp_device_list_find_remove(&list, driver_name, dev_name);
|
||||
|
||||
mp_device_list_free(list);
|
||||
|
||||
return found_device;
|
||||
}
|
||||
|
||||
MPDevice *
|
||||
mp_device_open(const char *path)
|
||||
{
|
||||
int fd = open(path, O_RDWR);
|
||||
if (fd == -1) {
|
||||
errno_printerr("open");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mp_device_new(fd);
|
||||
}
|
||||
|
||||
MPDevice *
|
||||
mp_device_new(int fd)
|
||||
{
|
||||
// Get the topology of the media device
|
||||
struct media_v2_topology topology = {};
|
||||
if (xioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology) == -1 ||
|
||||
topology.num_entities == 0) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create the device
|
||||
MPDevice *device = calloc(1, sizeof(MPDevice));
|
||||
device->fd = fd;
|
||||
device->entities =
|
||||
calloc(topology.num_entities, sizeof(struct media_v2_entity));
|
||||
device->num_entities = topology.num_entities;
|
||||
device->interfaces =
|
||||
calloc(topology.num_interfaces, sizeof(struct media_v2_interface));
|
||||
device->num_interfaces = topology.num_interfaces;
|
||||
device->pads = calloc(topology.num_pads, sizeof(struct media_v2_pad));
|
||||
device->num_pads = topology.num_pads;
|
||||
device->links = calloc(topology.num_links, sizeof(struct media_v2_link));
|
||||
device->num_links = topology.num_links;
|
||||
|
||||
// Get the actual devices and interfaces
|
||||
topology.ptr_entities = (uint64_t)device->entities;
|
||||
topology.ptr_interfaces = (uint64_t)device->interfaces;
|
||||
topology.ptr_pads = (uint64_t)device->pads;
|
||||
topology.ptr_links = (uint64_t)device->links;
|
||||
if (xioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology) == -1) {
|
||||
errno_printerr("MEDIA_IOC_G_TOPOLOGY");
|
||||
mp_device_close(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get device info
|
||||
if (xioctl(fd, MEDIA_IOC_DEVICE_INFO, &device->info) == -1) {
|
||||
errno_printerr("MEDIA_IOC_DEVICE_INFO");
|
||||
mp_device_close(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
void
|
||||
mp_device_close(MPDevice *device)
|
||||
{
|
||||
close(device->fd);
|
||||
free(device->entities);
|
||||
free(device->interfaces);
|
||||
free(device->pads);
|
||||
free(device->links);
|
||||
free(device);
|
||||
}
|
||||
|
||||
int
|
||||
mp_device_get_fd(const MPDevice *device)
|
||||
{
|
||||
return device->fd;
|
||||
}
|
||||
|
||||
bool
|
||||
mp_device_setup_entity_link(MPDevice *device,
|
||||
uint32_t source_entity_id,
|
||||
uint32_t sink_entity_id,
|
||||
uint32_t source_index,
|
||||
uint32_t sink_index,
|
||||
bool enabled)
|
||||
{
|
||||
struct media_link_desc link = {};
|
||||
link.flags = enabled ? MEDIA_LNK_FL_ENABLED : 0;
|
||||
link.source.entity = source_entity_id;
|
||||
link.source.index = source_index;
|
||||
link.sink.entity = sink_entity_id;
|
||||
link.sink.index = sink_index;
|
||||
if (xioctl(device->fd, MEDIA_IOC_SETUP_LINK, &link) == -1) {
|
||||
errno_printerr("MEDIA_IOC_SETUP_LINK");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
mp_device_setup_link(MPDevice *device,
|
||||
uint32_t source_pad_id,
|
||||
uint32_t sink_pad_id,
|
||||
bool enabled)
|
||||
{
|
||||
const struct media_v2_pad *source_pad =
|
||||
mp_device_get_pad(device, source_pad_id);
|
||||
g_return_val_if_fail(source_pad, false);
|
||||
|
||||
const struct media_v2_pad *sink_pad = mp_device_get_pad(device, sink_pad_id);
|
||||
g_return_val_if_fail(sink_pad, false);
|
||||
|
||||
return mp_device_setup_entity_link(
|
||||
device, source_pad->entity_id, sink_pad->entity_id, 0, 0, enabled);
|
||||
}
|
||||
|
||||
bool
|
||||
mp_entity_pad_set_format(MPDevice *device,
|
||||
const struct media_v2_entity *entity,
|
||||
uint32_t pad,
|
||||
MPMode *mode)
|
||||
{
|
||||
const struct media_v2_interface *interface =
|
||||
mp_device_find_entity_interface(device, entity->id);
|
||||
char path[260];
|
||||
if (!mp_find_device_path(interface->devnode, path, 260)) {
|
||||
g_printerr("Could not find path to %s\n", entity->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = open(path, O_WRONLY);
|
||||
if (fd == -1) {
|
||||
errno_printerr("open");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct v4l2_subdev_format fmt = {};
|
||||
fmt.pad = pad;
|
||||
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
||||
fmt.format.width = mode->width;
|
||||
fmt.format.height = mode->height;
|
||||
fmt.format.code = mp_pixel_format_to_v4l_bus_code(mode->pixel_format);
|
||||
fmt.format.field = V4L2_FIELD_ANY;
|
||||
if (xioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt) == -1) {
|
||||
errno_printerr("VIDIOC_SUBDEV_S_FMT");
|
||||
return false;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct media_v2_entity *
|
||||
mp_device_find_entity(const MPDevice *device, const char *driver_name)
|
||||
{
|
||||
int length = strlen(driver_name);
|
||||
|
||||
// Find the entity from the name
|
||||
for (uint32_t i = 0; i < device->num_entities; ++i) {
|
||||
if (strncmp(device->entities[i].name, driver_name, length) == 0) {
|
||||
return &device->entities[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct media_v2_entity *
|
||||
mp_device_find_entity_type(const MPDevice *device, const uint32_t type)
|
||||
{
|
||||
// Find the entity from the entity type
|
||||
for (uint32_t i = 0; i < device->num_entities; ++i) {
|
||||
if (device->entities[i].function == type) {
|
||||
return &device->entities[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct media_device_info *
|
||||
mp_device_get_info(const MPDevice *device)
|
||||
{
|
||||
return &device->info;
|
||||
}
|
||||
|
||||
const struct media_v2_entity *
|
||||
mp_device_get_entity(const MPDevice *device, uint32_t id)
|
||||
{
|
||||
for (int i = 0; i < device->num_entities; ++i) {
|
||||
if (device->entities[i].id == id) {
|
||||
return &device->entities[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct media_v2_entity *
|
||||
mp_device_get_entities(const MPDevice *device)
|
||||
{
|
||||
return device->entities;
|
||||
}
|
||||
|
||||
size_t
|
||||
mp_device_get_num_entities(const MPDevice *device)
|
||||
{
|
||||
return device->num_entities;
|
||||
}
|
||||
|
||||
const struct media_v2_interface *
|
||||
mp_device_find_entity_interface(const MPDevice *device, uint32_t entity_id)
|
||||
{
|
||||
// Find the interface through the link
|
||||
const struct media_v2_link *link = mp_device_find_link_to(device, entity_id);
|
||||
if (!link) {
|
||||
return NULL;
|
||||
}
|
||||
return mp_device_get_interface(device, link->source_id);
|
||||
}
|
||||
|
||||
const struct media_v2_interface *
|
||||
mp_device_get_interface(const MPDevice *device, uint32_t id)
|
||||
{
|
||||
for (int i = 0; i < device->num_interfaces; ++i) {
|
||||
if (device->interfaces[i].id == id) {
|
||||
return &device->interfaces[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct media_v2_interface *
|
||||
mp_device_get_interfaces(const MPDevice *device)
|
||||
{
|
||||
return device->interfaces;
|
||||
}
|
||||
|
||||
size_t
|
||||
mp_device_get_num_interfaces(const MPDevice *device)
|
||||
{
|
||||
return device->num_interfaces;
|
||||
}
|
||||
|
||||
const struct media_v2_pad *
|
||||
mp_device_get_pad_from_entity(const MPDevice *device, uint32_t entity_id)
|
||||
{
|
||||
for (int i = 0; i < device->num_pads; ++i) {
|
||||
if (device->pads[i].entity_id == entity_id) {
|
||||
return &device->pads[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct media_v2_pad *
|
||||
mp_device_get_pad(const MPDevice *device, uint32_t id)
|
||||
{
|
||||
for (int i = 0; i < device->num_pads; ++i) {
|
||||
if (device->pads[i].id == id) {
|
||||
return &device->pads[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct media_v2_pad *
|
||||
mp_device_get_pads(const MPDevice *device)
|
||||
{
|
||||
return device->pads;
|
||||
}
|
||||
|
||||
size_t
|
||||
mp_device_get_num_pads(const MPDevice *device)
|
||||
{
|
||||
return device->num_pads;
|
||||
}
|
||||
|
||||
const struct media_v2_link *
|
||||
mp_device_find_entity_link(const MPDevice *device, uint32_t entity_id)
|
||||
{
|
||||
const struct media_v2_pad *pad =
|
||||
mp_device_get_pad_from_entity(device, entity_id);
|
||||
const struct media_v2_link *link = mp_device_find_link_to(device, pad->id);
|
||||
if (link) {
|
||||
return link;
|
||||
}
|
||||
return mp_device_find_link_from(device, pad->id);
|
||||
}
|
||||
|
||||
const struct media_v2_link *
|
||||
mp_device_find_link_from(const MPDevice *device, uint32_t source)
|
||||
{
|
||||
for (int i = 0; i < device->num_links; ++i) {
|
||||
if (device->links[i].source_id == source) {
|
||||
return &device->links[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct media_v2_link *
|
||||
mp_device_find_link_to(const MPDevice *device, uint32_t sink)
|
||||
{
|
||||
for (int i = 0; i < device->num_links; ++i) {
|
||||
if (device->links[i].sink_id == sink) {
|
||||
return &device->links[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct media_v2_link *
|
||||
mp_device_find_link_between(const MPDevice *device, uint32_t source, uint32_t sink)
|
||||
{
|
||||
for (int i = 0; i < device->num_links; ++i) {
|
||||
if (device->links[i].source_id == source &&
|
||||
device->links[i].sink_id == sink) {
|
||||
return &device->links[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct media_v2_link *
|
||||
mp_device_get_link(const MPDevice *device, uint32_t id)
|
||||
{
|
||||
for (int i = 0; i < device->num_links; ++i) {
|
||||
if (device->links[i].id == id) {
|
||||
return &device->links[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct media_v2_link *
|
||||
mp_device_get_links(const MPDevice *device)
|
||||
{
|
||||
return device->links;
|
||||
}
|
||||
|
||||
size_t
|
||||
mp_device_get_num_links(const MPDevice *device)
|
||||
{
|
||||
return device->num_links;
|
||||
}
|
||||
|
||||
struct _MPDeviceList {
|
||||
MPDevice *device;
|
||||
MPDeviceList *next;
|
||||
char path[PATH_MAX];
|
||||
};
|
||||
|
||||
MPDeviceList *
|
||||
mp_device_list_new()
|
||||
{
|
||||
MPDeviceList *current = NULL;
|
||||
|
||||
// Enumerate media device files
|
||||
struct dirent *dir;
|
||||
DIR *d = opendir("/dev");
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if (strncmp(dir->d_name, "media", 5) == 0) {
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, PATH_MAX, "/dev/%s", dir->d_name);
|
||||
|
||||
MPDevice *device = mp_device_open(path);
|
||||
|
||||
if (device) {
|
||||
MPDeviceList *next = malloc(sizeof(MPDeviceList));
|
||||
next->device = device;
|
||||
next->next = current;
|
||||
memcpy(next->path, path, sizeof(path));
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
void
|
||||
mp_device_list_free(MPDeviceList *device_list)
|
||||
{
|
||||
while (device_list) {
|
||||
MPDeviceList *tmp = device_list;
|
||||
device_list = tmp->next;
|
||||
|
||||
mp_device_close(tmp->device);
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
MPDevice *
|
||||
mp_device_list_find_remove(MPDeviceList **list,
|
||||
const char *driver_name,
|
||||
const char *dev_name)
|
||||
{
|
||||
MPDevice *found_device = NULL;
|
||||
int length = strlen(driver_name);
|
||||
|
||||
while (*list) {
|
||||
MPDevice *device = mp_device_list_get(*list);
|
||||
const struct media_device_info *info = mp_device_get_info(device);
|
||||
|
||||
if (strncmp(info->driver, driver_name, length) == 0 &&
|
||||
mp_device_find_entity(device, dev_name)) {
|
||||
found_device = mp_device_list_remove(list);
|
||||
break;
|
||||
}
|
||||
|
||||
list = &(*list)->next;
|
||||
}
|
||||
|
||||
return found_device;
|
||||
}
|
||||
|
||||
MPDevice *
|
||||
mp_device_list_remove(MPDeviceList **device_list)
|
||||
{
|
||||
MPDevice *device = (*device_list)->device;
|
||||
|
||||
if ((*device_list)->next) {
|
||||
MPDeviceList *tmp = (*device_list)->next;
|
||||
**device_list = *tmp;
|
||||
free(tmp);
|
||||
} else {
|
||||
free(*device_list);
|
||||
*device_list = NULL;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
MPDevice *
|
||||
mp_device_list_get(const MPDeviceList *device_list)
|
||||
{
|
||||
return device_list->device;
|
||||
}
|
||||
|
||||
const char *
|
||||
mp_device_list_get_path(const MPDeviceList *device_list)
|
||||
{
|
||||
return device_list->path;
|
||||
}
|
||||
|
||||
MPDeviceList *
|
||||
mp_device_list_next(const MPDeviceList *device_list)
|
||||
{
|
||||
return device_list->next;
|
||||
}
|
83
src/device.h
83
src/device.h
@@ -1,83 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "mode.h"
|
||||
|
||||
#include <linux/media.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool
|
||||
mp_find_device_path(struct media_v2_intf_devnode devnode, char *path, int length);
|
||||
|
||||
typedef struct _MPDevice MPDevice;
|
||||
|
||||
MPDevice *mp_device_find(const char *driver_name, const char *dev_name);
|
||||
MPDevice *mp_device_open(const char *path);
|
||||
MPDevice *mp_device_new(int fd);
|
||||
void mp_device_close(MPDevice *device);
|
||||
|
||||
int mp_device_get_fd(const MPDevice *device);
|
||||
|
||||
bool mp_device_setup_entity_link(MPDevice *device,
|
||||
uint32_t source_entity_id,
|
||||
uint32_t sink_entity_id,
|
||||
uint32_t source_index,
|
||||
uint32_t sink_index,
|
||||
bool enabled);
|
||||
|
||||
bool mp_device_setup_link(MPDevice *device,
|
||||
uint32_t source_pad_id,
|
||||
uint32_t sink_pad_id,
|
||||
bool enabled);
|
||||
|
||||
bool mp_entity_pad_set_format(MPDevice *device,
|
||||
const struct media_v2_entity *entity,
|
||||
uint32_t pad,
|
||||
MPMode *mode);
|
||||
|
||||
const struct media_device_info *mp_device_get_info(const MPDevice *device);
|
||||
const struct media_v2_entity *mp_device_find_entity(const MPDevice *device,
|
||||
const char *driver_name);
|
||||
const struct media_v2_entity *mp_device_find_entity_type(const MPDevice *device,
|
||||
const uint32_t type);
|
||||
const struct media_v2_entity *mp_device_get_entity(const MPDevice *device,
|
||||
uint32_t id);
|
||||
const struct media_v2_entity *mp_device_get_entities(const MPDevice *device);
|
||||
size_t mp_device_get_num_entities(const MPDevice *device);
|
||||
const struct media_v2_interface *
|
||||
mp_device_find_entity_interface(const MPDevice *device, uint32_t entity_id);
|
||||
const struct media_v2_interface *mp_device_get_interface(const MPDevice *device,
|
||||
uint32_t id);
|
||||
const struct media_v2_interface *mp_device_get_interfaces(const MPDevice *device);
|
||||
size_t mp_device_get_num_interfaces(const MPDevice *device);
|
||||
const struct media_v2_pad *mp_device_get_pad_from_entity(const MPDevice *device,
|
||||
uint32_t entity_id);
|
||||
const struct media_v2_pad *mp_device_get_pad(const MPDevice *device, uint32_t id);
|
||||
const struct media_v2_pad *mp_device_get_pads(const MPDevice *device);
|
||||
size_t mp_device_get_num_pads(const MPDevice *device);
|
||||
const struct media_v2_link *mp_device_find_entity_link(const MPDevice *device,
|
||||
uint32_t entity_id);
|
||||
const struct media_v2_link *mp_device_find_link_from(const MPDevice *device,
|
||||
uint32_t source);
|
||||
const struct media_v2_link *mp_device_find_link_to(const MPDevice *device,
|
||||
uint32_t sink);
|
||||
const struct media_v2_link *
|
||||
mp_device_find_link_between(const MPDevice *device, uint32_t source, uint32_t sink);
|
||||
const struct media_v2_link *mp_device_get_link(const MPDevice *device, uint32_t id);
|
||||
const struct media_v2_link *mp_device_get_links(const MPDevice *device);
|
||||
size_t mp_device_get_num_links(const MPDevice *device);
|
||||
|
||||
typedef struct _MPDeviceList MPDeviceList;
|
||||
|
||||
MPDeviceList *mp_device_list_new();
|
||||
void mp_device_list_free(MPDeviceList *device_list);
|
||||
|
||||
MPDevice *mp_device_list_find_remove(MPDeviceList **device_list,
|
||||
const char *driver_name,
|
||||
const char *dev_name);
|
||||
MPDevice *mp_device_list_remove(MPDeviceList **device_list);
|
||||
|
||||
MPDevice *mp_device_list_get(const MPDeviceList *device_list);
|
||||
const char *mp_device_list_get_path(const MPDeviceList *device_list);
|
||||
MPDeviceList *mp_device_list_next(const MPDeviceList *device_list);
|
@@ -8,7 +8,7 @@
|
||||
#define TEX_COORD_ATTRIBUTE 1
|
||||
|
||||
struct _GLES2Debayer {
|
||||
MPPixelFormat format;
|
||||
int format;
|
||||
|
||||
GLuint frame_buffer;
|
||||
GLuint program;
|
||||
@@ -23,12 +23,13 @@ struct _GLES2Debayer {
|
||||
};
|
||||
|
||||
GLES2Debayer *
|
||||
gles2_debayer_new(MPPixelFormat format)
|
||||
gles2_debayer_new(int format)
|
||||
{
|
||||
if (format != MP_PIXEL_FMT_BGGR8 && format != MP_PIXEL_FMT_GBRG8 &&
|
||||
format != MP_PIXEL_FMT_GRBG8 && format != MP_PIXEL_FMT_RGGB8 &&
|
||||
format != MP_PIXEL_FMT_BGGR10P && format != MP_PIXEL_FMT_GBRG10P &&
|
||||
format != MP_PIXEL_FMT_GRBG10P && format != MP_PIXEL_FMT_RGGB10P) {
|
||||
uint32_t pixfmt = libmegapixels_format_to_v4l_pixfmt(format);
|
||||
if (pixfmt != V4L2_PIX_FMT_SBGGR8 && pixfmt != V4L2_PIX_FMT_SGBRG8 &&
|
||||
pixfmt != V4L2_PIX_FMT_SGRBG8 && pixfmt != V4L2_PIX_FMT_SRGGB8 &&
|
||||
pixfmt != V4L2_PIX_FMT_SBGGR10P && pixfmt != V4L2_PIX_FMT_SGBRG10P &&
|
||||
pixfmt != V4L2_PIX_FMT_SGRBG10P && pixfmt != V4L2_PIX_FMT_SRGGB10P) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -40,8 +41,8 @@ gles2_debayer_new(MPPixelFormat format)
|
||||
snprintf(format_def,
|
||||
64,
|
||||
"#define CFA_%s\n#define BITS_%d\n",
|
||||
mp_pixel_format_cfa(format),
|
||||
mp_pixel_format_bits_per_pixel(format));
|
||||
libmegapixels_format_cfa_pattern(format),
|
||||
libmegapixels_format_bits_per_pixel(format));
|
||||
|
||||
const GLchar *def[1] = { format_def };
|
||||
|
||||
@@ -74,7 +75,7 @@ gles2_debayer_new(MPPixelFormat format)
|
||||
self->uniform_texture = glGetUniformLocation(self->program, "texture");
|
||||
self->uniform_color_matrix =
|
||||
glGetUniformLocation(self->program, "color_matrix");
|
||||
if (mp_pixel_format_bits_per_pixel(self->format) == 10)
|
||||
if (libmegapixels_format_bits_per_pixel(self->format) == 10)
|
||||
self->uniform_row_length =
|
||||
glGetUniformLocation(self->program, "row_length");
|
||||
check_gl();
|
||||
@@ -97,6 +98,7 @@ gles2_debayer_free(GLES2Debayer *self)
|
||||
void
|
||||
gles2_debayer_use(GLES2Debayer *self)
|
||||
{
|
||||
assert(self != NULL);
|
||||
glUseProgram(self->program);
|
||||
check_gl();
|
||||
|
||||
@@ -159,15 +161,15 @@ gles2_debayer_configure(GLES2Debayer *self,
|
||||
}
|
||||
check_gl();
|
||||
|
||||
GLuint row_length = mp_pixel_format_width_to_bytes(self->format, src_width);
|
||||
if (mp_pixel_format_bits_per_pixel(self->format) == 10) {
|
||||
GLuint row_length = libmegapixels_mode_width_to_bytes(self->format, src_width);
|
||||
if (libmegapixels_format_bits_per_pixel(self->format) == 10) {
|
||||
assert(src_width % 4 == 0);
|
||||
glUniform1f(self->uniform_row_length, row_length);
|
||||
check_gl();
|
||||
}
|
||||
|
||||
GLuint padding_bytes =
|
||||
mp_pixel_format_width_to_padding(self->format, src_width);
|
||||
libmegapixels_mode_width_to_padding(self->format, src_width);
|
||||
GLfloat padding_ratio = (float)row_length / (row_length + padding_bytes);
|
||||
glUniform1f(self->uniform_padding_ratio, padding_ratio);
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
|
||||
typedef struct _GLES2Debayer GLES2Debayer;
|
||||
|
||||
GLES2Debayer *gles2_debayer_new(MPPixelFormat format);
|
||||
GLES2Debayer *gles2_debayer_new(int format);
|
||||
void gles2_debayer_free(GLES2Debayer *self);
|
||||
|
||||
void gles2_debayer_use(GLES2Debayer *self);
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#include "io_pipeline.h"
|
||||
|
||||
#include "camera.h"
|
||||
#include "device.h"
|
||||
#include "flash.h"
|
||||
#include "pipeline.h"
|
||||
#include "process_pipeline.h"
|
||||
@@ -14,62 +13,10 @@
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
struct media_link_info {
|
||||
unsigned int source_entity_id;
|
||||
unsigned int target_entity_id;
|
||||
char source_fname[260];
|
||||
char target_fname[260];
|
||||
};
|
||||
|
||||
struct camera_info {
|
||||
size_t device_index;
|
||||
|
||||
unsigned int pad_id;
|
||||
|
||||
char dev_fname[260];
|
||||
int fd;
|
||||
|
||||
MPCamera *camera;
|
||||
|
||||
MPFlash *flash;
|
||||
|
||||
int gain_ctrl;
|
||||
int gain_max;
|
||||
|
||||
bool has_auto_focus_continuous;
|
||||
bool has_auto_focus_start;
|
||||
|
||||
// unsigned int entity_id;
|
||||
// enum v4l2_buf_type type;
|
||||
|
||||
// char media_dev_fname[260];
|
||||
// char video_dev_fname[260];
|
||||
// int media_fd;
|
||||
|
||||
// struct mp_media_link media_links[MP_MAX_LINKS];
|
||||
// int num_media_links;
|
||||
|
||||
// int gain_ctrl;
|
||||
};
|
||||
|
||||
struct device_info {
|
||||
const char *media_dev_name; // owned by camera config
|
||||
const char *dev_name; // owned by camera config
|
||||
|
||||
MPDevice *device;
|
||||
|
||||
unsigned int interface_pad_id;
|
||||
|
||||
int video_fd;
|
||||
};
|
||||
|
||||
static struct camera_info cameras[MP_MAX_CAMERAS];
|
||||
|
||||
static struct device_info devices[MP_MAX_CAMERAS];
|
||||
static size_t num_devices = 0;
|
||||
|
||||
static const struct mp_camera_config *camera = NULL;
|
||||
static MPMode mode;
|
||||
libmegapixels_camera *io_camera = NULL;
|
||||
libmegapixels_mode *mode_capture = NULL;
|
||||
libmegapixels_mode *mode_preview = NULL;
|
||||
MPCamera *mpcamera = NULL;
|
||||
|
||||
static bool just_switched_mode = false;
|
||||
static int blank_frame_count = 0;
|
||||
@@ -100,240 +47,10 @@ static bool want_focus = false;
|
||||
static MPPipeline *pipeline;
|
||||
static GSource *capture_source;
|
||||
|
||||
static void
|
||||
mp_setup_media_link_pad_formats(struct device_info *dev_info,
|
||||
const struct mp_media_link_config media_links[],
|
||||
int num_media_links,
|
||||
MPMode *mode)
|
||||
{
|
||||
const struct media_v2_entity *entities[2];
|
||||
int ports[2];
|
||||
for (int i = 0; i < num_media_links; i++) {
|
||||
entities[0] = mp_device_find_entity(
|
||||
dev_info->device, (const char *)media_links[i].source_name);
|
||||
entities[1] = mp_device_find_entity(
|
||||
dev_info->device, (const char *)media_links[i].target_name);
|
||||
ports[0] = media_links[i].source_port;
|
||||
ports[1] = media_links[i].target_port;
|
||||
|
||||
for (int j = 0; j < 2; j++)
|
||||
if (!mp_entity_pad_set_format(
|
||||
dev_info->device, entities[j], ports[j], mode)) {
|
||||
g_printerr("Failed to set %s:%d format\n",
|
||||
entities[j]->name,
|
||||
ports[j]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
get_bridge_fd(const MPDevice *device)
|
||||
{
|
||||
const struct media_v2_entity *bridge =
|
||||
mp_device_find_entity_type(device, MEDIA_ENT_F_VID_IF_BRIDGE);
|
||||
if (!bridge) {
|
||||
g_printerr("Could not find device bridge entity\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct media_v2_interface *bridge_interface =
|
||||
mp_device_find_entity_interface(device, bridge->id);
|
||||
char dev_name[260];
|
||||
if (!mp_find_device_path(bridge_interface->devnode, dev_name, 260)) {
|
||||
g_printerr("Could not find bridge path\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bridge_fd = open(dev_name, O_RDWR);
|
||||
if (bridge_fd == -1) {
|
||||
g_printerr("Could not open %s: %s\n", dev_name, strerror(errno));
|
||||
}
|
||||
|
||||
return bridge_fd;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config)
|
||||
{
|
||||
// Find device info
|
||||
size_t device_index = 0;
|
||||
for (; device_index < num_devices; ++device_index) {
|
||||
if ((strcmp(config->media_dev_name,
|
||||
devices[device_index].media_dev_name) == 0) &&
|
||||
(strcmp(config->dev_name, devices[device_index].dev_name) ==
|
||||
0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (device_index == num_devices) {
|
||||
device_index = num_devices;
|
||||
|
||||
// Initialize new device
|
||||
struct device_info *info = &devices[device_index];
|
||||
info->media_dev_name = config->media_dev_name;
|
||||
info->dev_name = config->dev_name;
|
||||
info->device = mp_device_list_find_remove(
|
||||
device_list, info->media_dev_name, info->dev_name);
|
||||
if (!info->device) {
|
||||
g_printerr("Could not find /dev/media* node matching '%s'\n",
|
||||
info->media_dev_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const struct media_v2_entity *entity =
|
||||
mp_device_find_entity_type(info->device, MEDIA_ENT_F_IO_V4L);
|
||||
if (!entity) {
|
||||
g_printerr("Could not find device video entity\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const struct media_v2_entity *bridge = mp_device_find_entity_type(
|
||||
info->device, MEDIA_ENT_F_VID_IF_BRIDGE);
|
||||
if (!bridge) {
|
||||
g_printerr("Could not find device bridge entity\n");
|
||||
bridge = entity;
|
||||
}
|
||||
|
||||
const struct media_v2_pad *pad =
|
||||
mp_device_get_pad_from_entity(info->device, bridge->id);
|
||||
info->interface_pad_id = pad->id;
|
||||
|
||||
const struct media_v2_interface *interface =
|
||||
mp_device_find_entity_interface(info->device, entity->id);
|
||||
char dev_name[260];
|
||||
if (!mp_find_device_path(interface->devnode, dev_name, 260)) {
|
||||
g_printerr("Could not find video path\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
info->video_fd = open(dev_name, O_RDWR);
|
||||
if (info->video_fd == -1) {
|
||||
g_printerr("Could not open %s: %s\n",
|
||||
dev_name,
|
||||
strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
++num_devices;
|
||||
}
|
||||
|
||||
{
|
||||
struct camera_info *info = &cameras[config->index];
|
||||
struct device_info *dev_info = &devices[device_index];
|
||||
|
||||
info->device_index = device_index;
|
||||
|
||||
const struct media_v2_entity *entity =
|
||||
mp_device_find_entity(dev_info->device, config->dev_name);
|
||||
if (!entity) {
|
||||
g_printerr("Could not find camera entity matching '%s'\n",
|
||||
config->dev_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const struct media_v2_pad *pad =
|
||||
mp_device_get_pad_from_entity(dev_info->device, entity->id);
|
||||
|
||||
info->pad_id = pad->id;
|
||||
|
||||
// Make sure the camera starts out as disabled
|
||||
mp_device_setup_link(dev_info->device,
|
||||
info->pad_id,
|
||||
dev_info->interface_pad_id,
|
||||
false);
|
||||
|
||||
const struct media_v2_interface *interface =
|
||||
mp_device_find_entity_interface(dev_info->device,
|
||||
entity->id);
|
||||
|
||||
if (!mp_find_device_path(interface->devnode, info->dev_fname, 260)) {
|
||||
g_printerr("Could not find camera device path\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
info->fd = open(info->dev_fname, O_RDWR);
|
||||
if (info->fd == -1) {
|
||||
g_printerr("Could not open %s: %s\n",
|
||||
info->dev_fname,
|
||||
strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int bridge_fd = get_bridge_fd(dev_info->device);
|
||||
info->camera =
|
||||
mp_camera_new(dev_info->video_fd, info->fd, bridge_fd);
|
||||
|
||||
// Start with the capture format, this works around a bug with
|
||||
// the ov5640 driver where it won't allow setting the preview
|
||||
// format initially.
|
||||
MPMode mode = config->capture_mode;
|
||||
if (config->num_media_links)
|
||||
mp_setup_media_link_pad_formats(dev_info,
|
||||
config->media_links,
|
||||
config->num_media_links,
|
||||
&mode);
|
||||
mp_camera_set_mode(info->camera, &mode);
|
||||
|
||||
// Trigger continuous auto focus if the sensor supports it
|
||||
if (mp_camera_query_control(
|
||||
info->camera, V4L2_CID_FOCUS_AUTO, NULL)) {
|
||||
info->has_auto_focus_continuous = true;
|
||||
mp_camera_control_set_bool_bg(
|
||||
info->camera, V4L2_CID_FOCUS_AUTO, true);
|
||||
}
|
||||
if (mp_camera_query_control(
|
||||
info->camera, V4L2_CID_AUTO_FOCUS_START, NULL)) {
|
||||
info->has_auto_focus_start = true;
|
||||
}
|
||||
|
||||
MPControl control;
|
||||
if (mp_camera_query_control(info->camera, V4L2_CID_GAIN, &control)) {
|
||||
info->gain_ctrl = V4L2_CID_GAIN;
|
||||
info->gain_max = control.max;
|
||||
} else if (mp_camera_query_control(
|
||||
info->camera, V4L2_CID_ANALOGUE_GAIN, &control)) {
|
||||
info->gain_ctrl = V4L2_CID_ANALOGUE_GAIN;
|
||||
info->gain_max = control.max;
|
||||
}
|
||||
|
||||
// Setup flash
|
||||
if (config->flash_path[0]) {
|
||||
info->flash = mp_led_flash_from_path(config->flash_path);
|
||||
} else if (config->flash_display) {
|
||||
info->flash = mp_create_display_flash();
|
||||
} else {
|
||||
info->flash = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setup(MPPipeline *pipeline, const void *data)
|
||||
{
|
||||
for (size_t i = 0; i < MP_MAX_CAMERAS; ++i) {
|
||||
const struct mp_camera_config *config = mp_get_camera_config(i);
|
||||
if (!config) {
|
||||
break;
|
||||
}
|
||||
|
||||
MPDeviceList *device_list = mp_device_list_new();
|
||||
setup_camera(&device_list, config);
|
||||
mp_device_list_free(device_list);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clean_cameras()
|
||||
{
|
||||
for (size_t i = 0; i < MP_MAX_CAMERAS; ++i) {
|
||||
struct camera_info *info = &cameras[i];
|
||||
if (info->camera) {
|
||||
mp_camera_free(info->camera);
|
||||
info->camera = NULL;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -353,8 +70,6 @@ mp_io_pipeline_stop()
|
||||
g_source_destroy(capture_source);
|
||||
}
|
||||
|
||||
clean_cameras();
|
||||
|
||||
mp_pipeline_free(pipeline);
|
||||
|
||||
mp_process_pipeline_stop();
|
||||
@@ -363,21 +78,22 @@ mp_io_pipeline_stop()
|
||||
static void
|
||||
update_process_pipeline()
|
||||
{
|
||||
struct camera_info *info = &cameras[camera->index];
|
||||
|
||||
// Grab the latest control values
|
||||
if (!current_controls.gain_is_manual) {
|
||||
current_controls.gain =
|
||||
mp_camera_control_get_int32(info->camera, info->gain_ctrl);
|
||||
// current_controls.gain =
|
||||
// mp_camera_control_get_int32(info->camera,
|
||||
// info->gain_ctrl);
|
||||
}
|
||||
if (!current_controls.exposure_is_manual) {
|
||||
current_controls.exposure =
|
||||
mp_camera_control_get_int32(info->camera, V4L2_CID_EXPOSURE);
|
||||
// current_controls.exposure =
|
||||
// mp_camera_control_get_int32(info->camera,
|
||||
// V4L2_CID_EXPOSURE);
|
||||
}
|
||||
|
||||
MPControl control;
|
||||
float balance_red = 1.0f;
|
||||
float balance_blue = 1.0f;
|
||||
/*
|
||||
if (mp_camera_query_control(info->camera, V4L2_CID_RED_BALANCE, &control)) {
|
||||
int red = mp_camera_control_get_int32(info->camera,
|
||||
V4L2_CID_RED_BALANCE);
|
||||
@@ -386,23 +102,23 @@ update_process_pipeline()
|
||||
balance_red = (float)red / (float)control.max;
|
||||
balance_blue = (float)blue / (float)control.max;
|
||||
}
|
||||
*/
|
||||
|
||||
struct mp_process_pipeline_state pipeline_state = {
|
||||
.camera = camera,
|
||||
.mode = mode,
|
||||
.camera = io_camera,
|
||||
.burst_length = burst_length,
|
||||
.preview_width = preview_width,
|
||||
.preview_height = preview_height,
|
||||
.device_rotation = device_rotation,
|
||||
.gain_is_manual = current_controls.gain_is_manual,
|
||||
.gain = current_controls.gain,
|
||||
.gain_max = info->gain_max,
|
||||
.gain_max = 1, // TODO: Fix
|
||||
.balance_red = balance_red,
|
||||
.balance_blue = balance_blue,
|
||||
.exposure_is_manual = current_controls.exposure_is_manual,
|
||||
.exposure = current_controls.exposure,
|
||||
.has_auto_focus_continuous = info->has_auto_focus_continuous,
|
||||
.has_auto_focus_start = info->has_auto_focus_start,
|
||||
.has_auto_focus_continuous = false, // TODO: fix
|
||||
.has_auto_focus_start = false, // TODO: fix
|
||||
.flash_enabled = flash_enabled,
|
||||
};
|
||||
mp_process_pipeline_update_state(&pipeline_state);
|
||||
@@ -423,44 +139,39 @@ mp_io_pipeline_focus()
|
||||
static void
|
||||
capture(MPPipeline *pipeline, const void *data)
|
||||
{
|
||||
struct camera_info *info = &cameras[camera->index];
|
||||
struct device_info *dev_info = &devices[info->device_index];
|
||||
uint32_t gain;
|
||||
float gain_norm;
|
||||
|
||||
// Disable the autogain/exposure while taking the burst
|
||||
/* TODO: Fix
|
||||
mp_camera_control_set_int32(info->camera, V4L2_CID_AUTOGAIN, 0);
|
||||
mp_camera_control_set_int32(
|
||||
info->camera, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL);
|
||||
*/
|
||||
|
||||
// Get current gain to calculate a burst length;
|
||||
// with low gain there's 3, with the max automatic gain of the ov5640
|
||||
// the value seems to be 248 which creates a 5 frame burst
|
||||
// for manual gain you can go up to 11 frames
|
||||
gain = mp_camera_control_get_int32(info->camera, V4L2_CID_GAIN);
|
||||
gain_norm = (float)gain / (float)info->gain_max;
|
||||
burst_length = (int)fmax(sqrt(gain_norm) * 10, 2) + 1;
|
||||
gain = mp_camera_control_get_int32(io_camera, V4L2_CID_GAIN);
|
||||
// gain_norm = (float)gain / (float)mpcamera.gain_max;
|
||||
// burst_length = (int)fmax(sqrt(gain_norm) * 10, 2) + 1;
|
||||
captures_remaining = burst_length;
|
||||
|
||||
// Change camera mode for capturing
|
||||
mp_process_pipeline_sync();
|
||||
mp_camera_stop_capture(info->camera);
|
||||
|
||||
mode = camera->capture_mode;
|
||||
if (camera->num_media_links)
|
||||
mp_setup_media_link_pad_formats(dev_info,
|
||||
camera->media_links,
|
||||
camera->num_media_links,
|
||||
&mode);
|
||||
mp_camera_set_mode(info->camera, &mode);
|
||||
mp_camera_stop_capture(mpcamera);
|
||||
libmegapixels_select_mode(io_camera, mode_capture);
|
||||
just_switched_mode = true;
|
||||
|
||||
mp_camera_start_capture(info->camera);
|
||||
mp_camera_start_capture(mpcamera);
|
||||
|
||||
// Enable flash
|
||||
/* TODO: implement
|
||||
if (info->flash && flash_enabled) {
|
||||
mp_flash_enable(info->flash);
|
||||
}
|
||||
*/
|
||||
|
||||
update_process_pipeline();
|
||||
|
||||
@@ -476,9 +187,7 @@ mp_io_pipeline_capture()
|
||||
static void
|
||||
release_buffer(MPPipeline *pipeline, const uint32_t *buffer_index)
|
||||
{
|
||||
struct camera_info *info = &cameras[camera->index];
|
||||
|
||||
mp_camera_release_buffer(info->camera, *buffer_index);
|
||||
mp_camera_release_buffer(mpcamera, *buffer_index);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -493,20 +202,22 @@ mp_io_pipeline_release_buffer(uint32_t buffer_index)
|
||||
static pid_t focus_continuous_task = 0;
|
||||
static pid_t start_focus_task = 0;
|
||||
static void
|
||||
start_focus(struct camera_info *info)
|
||||
start_focus()
|
||||
{
|
||||
// only run 1 manual focus at once
|
||||
if (!mp_camera_check_task_complete(info->camera, start_focus_task) ||
|
||||
!mp_camera_check_task_complete(info->camera, focus_continuous_task))
|
||||
if (!mp_camera_check_task_complete(mpcamera, start_focus_task) ||
|
||||
!mp_camera_check_task_complete(mpcamera, focus_continuous_task))
|
||||
return;
|
||||
|
||||
if (info->has_auto_focus_continuous) {
|
||||
/* TODO: implement
|
||||
if (mpcamera.has_auto_focus_continuous) {
|
||||
focus_continuous_task = mp_camera_control_set_bool_bg(
|
||||
info->camera, V4L2_CID_FOCUS_AUTO, 1);
|
||||
} else if (info->has_auto_focus_start) {
|
||||
start_focus_task = mp_camera_control_set_bool_bg(
|
||||
info->camera, V4L2_CID_AUTO_FOCUS_START, 1);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -516,11 +227,9 @@ update_controls()
|
||||
if (captures_remaining > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct camera_info *info = &cameras[camera->index];
|
||||
|
||||
/* TODO: implement
|
||||
if (want_focus) {
|
||||
start_focus(info);
|
||||
start_focus(mpcamera);
|
||||
want_focus = false;
|
||||
}
|
||||
|
||||
@@ -552,6 +261,7 @@ update_controls()
|
||||
}
|
||||
|
||||
current_controls = desired_controls;
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -565,8 +275,8 @@ on_frame(MPBuffer buffer, void *_data)
|
||||
if (just_switched_mode) {
|
||||
if (blank_frame_count < 20) {
|
||||
// Only check a 10x10 area
|
||||
size_t test_size =
|
||||
MIN(10, mode.width) * MIN(10, mode.height);
|
||||
size_t test_size = MIN(10, io_camera->current_mode->width) *
|
||||
MIN(10, io_camera->current_mode->height);
|
||||
|
||||
bool image_is_blank = true;
|
||||
for (size_t i = 0; i < test_size; ++i) {
|
||||
@@ -594,67 +304,41 @@ on_frame(MPBuffer buffer, void *_data)
|
||||
--captures_remaining;
|
||||
|
||||
if (captures_remaining == 0) {
|
||||
struct camera_info *info = &cameras[camera->index];
|
||||
struct device_info *dev_info = &devices[info->device_index];
|
||||
|
||||
// Restore the auto exposure and gain if needed
|
||||
if (!current_controls.exposure_is_manual) {
|
||||
mp_camera_control_set_int32_bg(
|
||||
info->camera,
|
||||
io_camera,
|
||||
V4L2_CID_EXPOSURE_AUTO,
|
||||
V4L2_EXPOSURE_AUTO);
|
||||
}
|
||||
|
||||
/* TODO: implement
|
||||
if (!current_controls.gain_is_manual) {
|
||||
mp_camera_control_set_bool_bg(
|
||||
info->camera, V4L2_CID_AUTOGAIN, true);
|
||||
}
|
||||
*/
|
||||
|
||||
// Go back to preview mode
|
||||
mp_process_pipeline_sync();
|
||||
mp_camera_stop_capture(info->camera);
|
||||
|
||||
mode = camera->preview_mode;
|
||||
if (camera->num_media_links)
|
||||
mp_setup_media_link_pad_formats(
|
||||
dev_info,
|
||||
camera->media_links,
|
||||
camera->num_media_links,
|
||||
&mode);
|
||||
mp_camera_set_mode(info->camera, &mode);
|
||||
mp_camera_stop_capture(mpcamera);
|
||||
libmegapixels_select_mode(io_camera, mode_preview);
|
||||
just_switched_mode = true;
|
||||
|
||||
mp_camera_start_capture(info->camera);
|
||||
mp_camera_start_capture(mpcamera);
|
||||
|
||||
// Disable flash
|
||||
/* TODO: implement
|
||||
if (info->flash && flash_enabled) {
|
||||
mp_flash_disable(info->flash);
|
||||
}
|
||||
*/
|
||||
|
||||
update_process_pipeline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mp_setup_media_link(struct device_info *dev_info,
|
||||
const struct mp_media_link_config *cfg,
|
||||
bool enable)
|
||||
{
|
||||
const struct media_v2_entity *source_entity =
|
||||
mp_device_find_entity(dev_info->device, cfg->source_name);
|
||||
|
||||
const struct media_v2_entity *target_entity =
|
||||
mp_device_find_entity(dev_info->device, cfg->target_name);
|
||||
|
||||
mp_device_setup_entity_link(dev_info->device,
|
||||
source_entity->id,
|
||||
target_entity->id,
|
||||
cfg->source_port,
|
||||
cfg->target_port,
|
||||
enable);
|
||||
}
|
||||
|
||||
static void
|
||||
update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state)
|
||||
{
|
||||
@@ -662,24 +346,13 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state)
|
||||
// whether this state change actually changes anything.
|
||||
bool has_changed = false;
|
||||
|
||||
if (camera != state->camera) {
|
||||
if (io_camera != state->camera) {
|
||||
has_changed = true;
|
||||
|
||||
if (camera) {
|
||||
struct camera_info *info = &cameras[camera->index];
|
||||
struct device_info *dev_info = &devices[info->device_index];
|
||||
|
||||
if (io_camera != NULL) {
|
||||
mp_process_pipeline_sync();
|
||||
mp_camera_stop_capture(info->camera);
|
||||
mp_device_setup_link(dev_info->device,
|
||||
info->pad_id,
|
||||
dev_info->interface_pad_id,
|
||||
false);
|
||||
|
||||
// Disable media links
|
||||
for (int i = 0; i < camera->num_media_links; i++)
|
||||
mp_setup_media_link(
|
||||
dev_info, &camera->media_links[i], false);
|
||||
mp_camera_stop_capture(mpcamera);
|
||||
libmegapixels_close(io_camera);
|
||||
}
|
||||
|
||||
if (capture_source) {
|
||||
@@ -687,47 +360,52 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state)
|
||||
capture_source = NULL;
|
||||
}
|
||||
|
||||
camera = state->camera;
|
||||
io_camera = state->camera;
|
||||
if (io_camera) {
|
||||
libmegapixels_open(io_camera);
|
||||
mpcamera = mp_camera_new(io_camera);
|
||||
mode_preview = NULL;
|
||||
mode_capture = NULL;
|
||||
for (int m = 0; m < io_camera->num_modes; m++) {
|
||||
if (io_camera->modes[m]->rate > 29) {
|
||||
mode_preview = io_camera->modes[m];
|
||||
break;
|
||||
}
|
||||
}
|
||||
long area = 0;
|
||||
for (int m = 0; m < io_camera->num_modes; m++) {
|
||||
long this_pixels = io_camera->modes[m]->width *
|
||||
io_camera->modes[m]->height;
|
||||
|
||||
if (camera) {
|
||||
struct camera_info *info = &cameras[camera->index];
|
||||
struct device_info *dev_info = &devices[info->device_index];
|
||||
if (this_pixels > area) {
|
||||
area = this_pixels;
|
||||
mode_capture = io_camera->modes[m];
|
||||
}
|
||||
}
|
||||
if (mode_preview != NULL) {
|
||||
if (io_camera->video_fd == 0) {
|
||||
libmegapixels_open(io_camera);
|
||||
}
|
||||
libmegapixels_select_mode(io_camera, mode_preview);
|
||||
}
|
||||
|
||||
mp_device_setup_link(dev_info->device,
|
||||
info->pad_id,
|
||||
dev_info->interface_pad_id,
|
||||
true);
|
||||
|
||||
// Enable media links
|
||||
for (int i = 0; i < camera->num_media_links; i++)
|
||||
mp_setup_media_link(
|
||||
dev_info, &camera->media_links[i], true);
|
||||
|
||||
mode = camera->preview_mode;
|
||||
if (camera->num_media_links)
|
||||
mp_setup_media_link_pad_formats(
|
||||
dev_info,
|
||||
camera->media_links,
|
||||
camera->num_media_links,
|
||||
&mode);
|
||||
mp_camera_set_mode(info->camera, &mode);
|
||||
|
||||
mp_camera_start_capture(info->camera);
|
||||
mp_camera_start_capture(mpcamera);
|
||||
capture_source = mp_pipeline_add_capture_source(
|
||||
pipeline, info->camera, on_frame, NULL);
|
||||
pipeline, mpcamera, on_frame, NULL);
|
||||
|
||||
current_controls.gain_is_manual =
|
||||
mp_camera_control_get_bool(info->camera,
|
||||
mp_camera_control_get_bool(io_camera,
|
||||
V4L2_CID_AUTOGAIN) == 0;
|
||||
current_controls.gain = mp_camera_control_get_int32(
|
||||
info->camera, info->gain_ctrl);
|
||||
// current_controls.gain =
|
||||
// mp_camera_control_get_int32(camera,
|
||||
// info->gain_ctrl);
|
||||
|
||||
current_controls.exposure_is_manual =
|
||||
mp_camera_control_get_int32(
|
||||
info->camera, V4L2_CID_EXPOSURE_AUTO) ==
|
||||
V4L2_EXPOSURE_MANUAL;
|
||||
current_controls.exposure = mp_camera_control_get_int32(
|
||||
info->camera, V4L2_CID_EXPOSURE);
|
||||
// current_controls.exposure_is_manual =
|
||||
// mp_camera_control_get_int32(
|
||||
// info->camera, V4L2_CID_EXPOSURE_AUTO) ==
|
||||
// V4L2_EXPOSURE_MANUAL;
|
||||
// current_controls.exposure = mp_camera_control_get_int32(
|
||||
// info->camera, V4L2_CID_EXPOSURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,7 +419,7 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state)
|
||||
preview_height = state->preview_height;
|
||||
device_rotation = state->device_rotation;
|
||||
|
||||
if (camera) {
|
||||
if (io_camera) {
|
||||
struct control_state previous_desired = desired_controls;
|
||||
|
||||
desired_controls.gain_is_manual = state->gain_is_manual;
|
||||
|
@@ -1,9 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "camera_config.h"
|
||||
#include <libmegapixels.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct mp_io_pipeline_state {
|
||||
const struct mp_camera_config *camera;
|
||||
libmegapixels_camera *camera;
|
||||
|
||||
int burst_length;
|
||||
|
||||
|
53
src/main.c
53
src/main.c
@@ -1,6 +1,5 @@
|
||||
#include "main.h"
|
||||
|
||||
#include "camera_config.h"
|
||||
#include "flash.h"
|
||||
#include "gl_util.h"
|
||||
#include "io_pipeline.h"
|
||||
@@ -21,6 +20,7 @@
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <gdk/x11/gdkx.h>
|
||||
#endif
|
||||
#include <libmegapixels.h>
|
||||
#include <limits.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/media.h>
|
||||
@@ -47,9 +47,12 @@ RENDERDOC_API_1_1_2 *rdoc_api = NULL;
|
||||
|
||||
enum user_control { USER_CONTROL_ISO, USER_CONTROL_SHUTTER };
|
||||
|
||||
libmegapixels_devconfig *configuration = { 0 };
|
||||
libmegapixels_camera *camera = NULL;
|
||||
int current_camera_index = 0;
|
||||
|
||||
static bool camera_is_initialized = false;
|
||||
static const struct mp_camera_config *camera = NULL;
|
||||
static MPMode mode;
|
||||
struct mp_main_state current_state = { 0 };
|
||||
|
||||
static int preview_width = -1;
|
||||
static int preview_height = -1;
|
||||
@@ -128,7 +131,8 @@ update_io_pipeline()
|
||||
mp_io_pipeline_update_state(&io_state);
|
||||
|
||||
// Make the right settings available for the camera
|
||||
gtk_widget_set_visible(flash_button, camera->has_flash);
|
||||
// TODO: Implement flash again
|
||||
gtk_widget_set_visible(flash_button, false);
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -138,8 +142,8 @@ update_state(const struct mp_main_state *state)
|
||||
camera_is_initialized = true;
|
||||
}
|
||||
|
||||
if (camera == state->camera) {
|
||||
mode = state->mode;
|
||||
if (current_state.camera == state->camera) {
|
||||
current_state.mode = state->mode;
|
||||
|
||||
if (!gain_is_manual) {
|
||||
gain = state->gain;
|
||||
@@ -154,8 +158,8 @@ update_state(const struct mp_main_state *state)
|
||||
has_auto_focus_start = state->has_auto_focus_start;
|
||||
}
|
||||
|
||||
preview_buffer_width = state->image_width;
|
||||
preview_buffer_height = state->image_height;
|
||||
preview_buffer_width = state->mode->width;
|
||||
preview_buffer_height = state->mode->height;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -693,16 +697,11 @@ preview_pressed(GtkGestureClick *gesture, int n_press, double x, double y)
|
||||
static void
|
||||
run_camera_switch_action(GSimpleAction *action, GVariant *param, gpointer user_data)
|
||||
{
|
||||
size_t next_index = camera->index + 1;
|
||||
const struct mp_camera_config *next_camera =
|
||||
mp_get_camera_config(next_index);
|
||||
|
||||
if (!next_camera) {
|
||||
next_index = 0;
|
||||
next_camera = mp_get_camera_config(next_index);
|
||||
current_camera_index++;
|
||||
if (current_camera_index > configuration->count) {
|
||||
current_camera_index = 0;
|
||||
}
|
||||
|
||||
camera = next_camera;
|
||||
camera = configuration->cameras[current_camera_index];
|
||||
update_io_pipeline();
|
||||
}
|
||||
|
||||
@@ -828,11 +827,14 @@ open_iso_controls(GtkWidget *button, gpointer user_data)
|
||||
static void
|
||||
set_shutter(double value)
|
||||
{
|
||||
// TODO: Implement shutter in libmegapixels
|
||||
/*
|
||||
int new_exposure = (int)(value / 360.0 * camera->capture_mode.height);
|
||||
if (new_exposure != exposure) {
|
||||
exposure = new_exposure;
|
||||
update_io_pipeline();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -874,7 +876,8 @@ on_realize(GtkWidget *window, gpointer *data)
|
||||
GtkNative *native = gtk_widget_get_native(window);
|
||||
mp_process_pipeline_init_gl(gtk_native_get_surface(native));
|
||||
|
||||
camera = mp_get_camera_config(0);
|
||||
current_camera_index = 0;
|
||||
camera = configuration->cameras[0];
|
||||
update_io_pipeline();
|
||||
}
|
||||
|
||||
@@ -1338,9 +1341,19 @@ main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!mp_load_config())
|
||||
char configfile[PATH_MAX];
|
||||
libmegapixels_init(&configuration);
|
||||
if (libmegapixels_find_config(configfile)) {
|
||||
if (!libmegapixels_load_file(configuration, configfile)) {
|
||||
fprintf(stderr, "Could not load config\n");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (!libmegapixels_load_uvc(configuration)) {
|
||||
fprintf(stderr, "No config found\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
setenv("LC_NUMERIC", "C", 1);
|
||||
|
||||
|
@@ -1,16 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "camera_config.h"
|
||||
#include "gtk/gtk.h"
|
||||
#include "process_pipeline.h"
|
||||
#include "zbar_pipeline.h"
|
||||
|
||||
struct mp_main_state {
|
||||
const struct mp_camera_config *camera;
|
||||
MPMode mode;
|
||||
|
||||
int image_width;
|
||||
int image_height;
|
||||
libmegapixels_mode *mode;
|
||||
|
||||
bool gain_is_manual;
|
||||
int gain;
|
||||
|
276
src/mode.c
276
src/mode.c
@@ -1,276 +0,0 @@
|
||||
#include "mode.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const char *pixel_format_names[MP_PIXEL_FMT_MAX] = {
|
||||
"unsupported", "BGGR8", "GBRG8", "GRBG8", "RGGB8", "BGGR10P",
|
||||
"GBRG10P", "GRBG10P", "RGGB10P", "UYVY", "YUYV",
|
||||
};
|
||||
|
||||
const char *
|
||||
mp_pixel_format_to_str(uint32_t pixel_format)
|
||||
{
|
||||
g_return_val_if_fail(pixel_format < MP_PIXEL_FMT_MAX, "INVALID");
|
||||
return pixel_format_names[pixel_format];
|
||||
}
|
||||
|
||||
MPPixelFormat
|
||||
mp_pixel_format_from_str(const char *name)
|
||||
{
|
||||
for (MPPixelFormat i = 0; i < MP_PIXEL_FMT_MAX; ++i) {
|
||||
if (strcasecmp(pixel_format_names[i], name) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
g_return_val_if_reached(MP_PIXEL_FMT_UNSUPPORTED);
|
||||
}
|
||||
|
||||
static const uint32_t pixel_format_v4l_pixel_formats[MP_PIXEL_FMT_MAX] = {
|
||||
0,
|
||||
V4L2_PIX_FMT_SBGGR8,
|
||||
V4L2_PIX_FMT_SGBRG8,
|
||||
V4L2_PIX_FMT_SGRBG8,
|
||||
V4L2_PIX_FMT_SRGGB8,
|
||||
V4L2_PIX_FMT_SBGGR10P,
|
||||
V4L2_PIX_FMT_SGBRG10P,
|
||||
V4L2_PIX_FMT_SGRBG10P,
|
||||
V4L2_PIX_FMT_SRGGB10P,
|
||||
V4L2_PIX_FMT_UYVY,
|
||||
V4L2_PIX_FMT_YUYV,
|
||||
};
|
||||
|
||||
uint32_t
|
||||
mp_pixel_format_to_v4l_pixel_format(MPPixelFormat pixel_format)
|
||||
{
|
||||
g_return_val_if_fail(pixel_format < MP_PIXEL_FMT_MAX, 0);
|
||||
return pixel_format_v4l_pixel_formats[pixel_format];
|
||||
}
|
||||
|
||||
MPPixelFormat
|
||||
mp_pixel_format_from_v4l_pixel_format(uint32_t v4l_pixel_format)
|
||||
{
|
||||
for (MPPixelFormat i = 0; i < MP_PIXEL_FMT_MAX; ++i) {
|
||||
if (pixel_format_v4l_pixel_formats[i] == v4l_pixel_format) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return MP_PIXEL_FMT_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static const uint32_t pixel_format_v4l_bus_codes[MP_PIXEL_FMT_MAX] = {
|
||||
0,
|
||||
MEDIA_BUS_FMT_SBGGR8_1X8,
|
||||
MEDIA_BUS_FMT_SGBRG8_1X8,
|
||||
MEDIA_BUS_FMT_SGRBG8_1X8,
|
||||
MEDIA_BUS_FMT_SRGGB8_1X8,
|
||||
MEDIA_BUS_FMT_SBGGR10_1X10,
|
||||
MEDIA_BUS_FMT_SGBRG10_1X10,
|
||||
MEDIA_BUS_FMT_SGRBG10_1X10,
|
||||
MEDIA_BUS_FMT_SRGGB10_1X10,
|
||||
MEDIA_BUS_FMT_UYVY8_2X8,
|
||||
MEDIA_BUS_FMT_YUYV8_2X8,
|
||||
};
|
||||
|
||||
uint32_t
|
||||
mp_pixel_format_to_v4l_bus_code(MPPixelFormat pixel_format)
|
||||
{
|
||||
g_return_val_if_fail(pixel_format < MP_PIXEL_FMT_MAX, 0);
|
||||
return pixel_format_v4l_bus_codes[pixel_format];
|
||||
}
|
||||
|
||||
MPPixelFormat
|
||||
mp_pixel_format_from_v4l_bus_code(uint32_t v4l_bus_code)
|
||||
{
|
||||
for (MPPixelFormat i = 0; i < MP_PIXEL_FMT_MAX; ++i) {
|
||||
if (pixel_format_v4l_bus_codes[i] == v4l_bus_code) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return MP_PIXEL_FMT_UNSUPPORTED;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mp_pixel_format_bits_per_pixel(MPPixelFormat pixel_format)
|
||||
{
|
||||
g_return_val_if_fail(pixel_format < MP_PIXEL_FMT_MAX, 0);
|
||||
switch (pixel_format) {
|
||||
case MP_PIXEL_FMT_BGGR8:
|
||||
case MP_PIXEL_FMT_GBRG8:
|
||||
case MP_PIXEL_FMT_GRBG8:
|
||||
case MP_PIXEL_FMT_RGGB8:
|
||||
return 8;
|
||||
case MP_PIXEL_FMT_BGGR10P:
|
||||
case MP_PIXEL_FMT_GBRG10P:
|
||||
case MP_PIXEL_FMT_GRBG10P:
|
||||
case MP_PIXEL_FMT_RGGB10P:
|
||||
return 10;
|
||||
case MP_PIXEL_FMT_UYVY:
|
||||
case MP_PIXEL_FMT_YUYV:
|
||||
return 16;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mp_pixel_format_pixel_depth(MPPixelFormat pixel_format)
|
||||
{
|
||||
g_return_val_if_fail(pixel_format < MP_PIXEL_FMT_MAX, 0);
|
||||
switch (pixel_format) {
|
||||
case MP_PIXEL_FMT_BGGR8:
|
||||
case MP_PIXEL_FMT_GBRG8:
|
||||
case MP_PIXEL_FMT_GRBG8:
|
||||
case MP_PIXEL_FMT_RGGB8:
|
||||
case MP_PIXEL_FMT_UYVY:
|
||||
case MP_PIXEL_FMT_YUYV:
|
||||
return 8;
|
||||
case MP_PIXEL_FMT_GBRG10P:
|
||||
case MP_PIXEL_FMT_GRBG10P:
|
||||
case MP_PIXEL_FMT_RGGB10P:
|
||||
case MP_PIXEL_FMT_BGGR10P:
|
||||
return 10;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
mp_pixel_format_cfa(MPPixelFormat pixel_format)
|
||||
{
|
||||
g_return_val_if_fail(pixel_format < MP_PIXEL_FMT_MAX, 0);
|
||||
switch (pixel_format) {
|
||||
case MP_PIXEL_FMT_BGGR8:
|
||||
case MP_PIXEL_FMT_BGGR10P:
|
||||
return "BGGR";
|
||||
break;
|
||||
case MP_PIXEL_FMT_GBRG8:
|
||||
case MP_PIXEL_FMT_GBRG10P:
|
||||
return "GBRG";
|
||||
break;
|
||||
case MP_PIXEL_FMT_GRBG8:
|
||||
case MP_PIXEL_FMT_GRBG10P:
|
||||
return "GRBG";
|
||||
break;
|
||||
case MP_PIXEL_FMT_RGGB8:
|
||||
case MP_PIXEL_FMT_RGGB10P:
|
||||
return "RGGB";
|
||||
break;
|
||||
case MP_PIXEL_FMT_UYVY:
|
||||
return "UYUV";
|
||||
break;
|
||||
case MP_PIXEL_FMT_YUYV:
|
||||
return "YUYV";
|
||||
break;
|
||||
default:
|
||||
return "unsupported";
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
mp_pixel_format_cfa_pattern(MPPixelFormat pixel_format)
|
||||
{
|
||||
g_return_val_if_fail(pixel_format < MP_PIXEL_FMT_MAX, 0);
|
||||
switch (pixel_format) {
|
||||
case MP_PIXEL_FMT_BGGR8:
|
||||
case MP_PIXEL_FMT_BGGR10P:
|
||||
return "\002\001\001\000";
|
||||
break;
|
||||
case MP_PIXEL_FMT_GBRG8:
|
||||
case MP_PIXEL_FMT_GBRG10P:
|
||||
return "\001\002\000\001";
|
||||
break;
|
||||
case MP_PIXEL_FMT_GRBG8:
|
||||
case MP_PIXEL_FMT_GRBG10P:
|
||||
return "\001\000\002\001";
|
||||
break;
|
||||
case MP_PIXEL_FMT_RGGB8:
|
||||
case MP_PIXEL_FMT_RGGB10P:
|
||||
return "\000\001\001\002";
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mp_pixel_format_width_to_bytes(MPPixelFormat pixel_format, uint32_t width)
|
||||
{
|
||||
uint32_t bits_per_pixel = mp_pixel_format_bits_per_pixel(pixel_format);
|
||||
uint64_t bits_per_width = width * (uint64_t)bits_per_pixel;
|
||||
|
||||
uint64_t remainder = bits_per_width % 8;
|
||||
if (remainder == 0)
|
||||
return bits_per_width / 8;
|
||||
|
||||
return (bits_per_width + 8 - remainder) / 8;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mp_pixel_format_width_to_padding(MPPixelFormat pixel_format, uint32_t width)
|
||||
{
|
||||
uint64_t bytes_per_width =
|
||||
mp_pixel_format_width_to_bytes(pixel_format, width);
|
||||
|
||||
uint64_t remainder = bytes_per_width % 8;
|
||||
if (remainder == 0)
|
||||
return remainder;
|
||||
|
||||
return 8 - remainder;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mp_pixel_format_width_to_colors(MPPixelFormat pixel_format, uint32_t width)
|
||||
{
|
||||
g_return_val_if_fail(pixel_format < MP_PIXEL_FMT_MAX, 0);
|
||||
switch (pixel_format) {
|
||||
case MP_PIXEL_FMT_BGGR8:
|
||||
case MP_PIXEL_FMT_GBRG8:
|
||||
case MP_PIXEL_FMT_GRBG8:
|
||||
case MP_PIXEL_FMT_RGGB8:
|
||||
return width / 2;
|
||||
case MP_PIXEL_FMT_BGGR10P:
|
||||
case MP_PIXEL_FMT_GBRG10P:
|
||||
case MP_PIXEL_FMT_GRBG10P:
|
||||
case MP_PIXEL_FMT_RGGB10P:
|
||||
return width / 2 * 5;
|
||||
case MP_PIXEL_FMT_UYVY:
|
||||
case MP_PIXEL_FMT_YUYV:
|
||||
return width;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mp_pixel_format_height_to_colors(MPPixelFormat pixel_format, uint32_t height)
|
||||
{
|
||||
g_return_val_if_fail(pixel_format < MP_PIXEL_FMT_MAX, 0);
|
||||
switch (pixel_format) {
|
||||
case MP_PIXEL_FMT_BGGR8:
|
||||
case MP_PIXEL_FMT_GBRG8:
|
||||
case MP_PIXEL_FMT_GRBG8:
|
||||
case MP_PIXEL_FMT_RGGB8:
|
||||
case MP_PIXEL_FMT_BGGR10P:
|
||||
case MP_PIXEL_FMT_GBRG10P:
|
||||
case MP_PIXEL_FMT_GRBG10P:
|
||||
case MP_PIXEL_FMT_RGGB10P:
|
||||
return height / 2;
|
||||
case MP_PIXEL_FMT_UYVY:
|
||||
case MP_PIXEL_FMT_YUYV:
|
||||
return height;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
mp_mode_is_equivalent(const MPMode *m1, const MPMode *m2)
|
||||
{
|
||||
return m1->pixel_format == m2->pixel_format &&
|
||||
m1->frame_interval.numerator == m2->frame_interval.numerator &&
|
||||
m1->frame_interval.denominator == m2->frame_interval.denominator &&
|
||||
m1->width == m2->width && m1->height == m2->height;
|
||||
}
|
57
src/mode.h
57
src/mode.h
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <linux/v4l2-subdev.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
MP_PIXEL_FMT_UNSUPPORTED,
|
||||
MP_PIXEL_FMT_BGGR8,
|
||||
MP_PIXEL_FMT_GBRG8,
|
||||
MP_PIXEL_FMT_GRBG8,
|
||||
MP_PIXEL_FMT_RGGB8,
|
||||
MP_PIXEL_FMT_BGGR10P,
|
||||
MP_PIXEL_FMT_GBRG10P,
|
||||
MP_PIXEL_FMT_GRBG10P,
|
||||
MP_PIXEL_FMT_RGGB10P,
|
||||
MP_PIXEL_FMT_UYVY,
|
||||
MP_PIXEL_FMT_YUYV,
|
||||
|
||||
MP_PIXEL_FMT_MAX,
|
||||
} MPPixelFormat;
|
||||
|
||||
const char *mp_pixel_format_to_str(MPPixelFormat pixel_format);
|
||||
MPPixelFormat mp_pixel_format_from_str(const char *str);
|
||||
|
||||
MPPixelFormat mp_pixel_format_from_v4l_pixel_format(uint32_t v4l_pixel_format);
|
||||
MPPixelFormat mp_pixel_format_from_v4l_bus_code(uint32_t v4l_bus_code);
|
||||
uint32_t mp_pixel_format_to_v4l_pixel_format(MPPixelFormat pixel_format);
|
||||
uint32_t mp_pixel_format_to_v4l_bus_code(MPPixelFormat pixel_format);
|
||||
|
||||
uint32_t mp_pixel_format_bits_per_pixel(MPPixelFormat pixel_format);
|
||||
uint32_t mp_pixel_format_pixel_depth(MPPixelFormat pixel_format);
|
||||
const char *mp_pixel_format_cfa(MPPixelFormat pixel_format);
|
||||
const char *mp_pixel_format_cfa_pattern(MPPixelFormat pixel_format);
|
||||
uint32_t mp_pixel_format_width_to_bytes(MPPixelFormat pixel_format, uint32_t width);
|
||||
uint32_t mp_pixel_format_width_to_padding(MPPixelFormat pixel_format,
|
||||
uint32_t width);
|
||||
uint32_t mp_pixel_format_width_to_colors(MPPixelFormat pixel_format, uint32_t width);
|
||||
uint32_t mp_pixel_format_height_to_colors(MPPixelFormat pixel_format,
|
||||
uint32_t height);
|
||||
|
||||
typedef struct {
|
||||
MPPixelFormat pixel_format;
|
||||
|
||||
struct v4l2_fract frame_interval;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
} MPMode;
|
||||
|
||||
bool mp_mode_is_equivalent(const MPMode *m1, const MPMode *m2);
|
||||
|
||||
typedef struct _MPModeList MPModeList;
|
||||
|
||||
struct _MPModeList {
|
||||
MPMode mode;
|
||||
MPModeList *next;
|
||||
};
|
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "camera.h"
|
||||
#include "device.h"
|
||||
#include <glib.h>
|
||||
|
||||
typedef struct _MPPipeline MPPipeline;
|
||||
|
@@ -1,6 +1,5 @@
|
||||
#include "process_pipeline.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "gles2_debayer.h"
|
||||
#include "io_pipeline.h"
|
||||
#include "main.h"
|
||||
@@ -10,15 +9,20 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <math.h>
|
||||
#include <tiffio.h>
|
||||
#ifndef SYSCONFDIR
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "dcp.h"
|
||||
#include "gl_util.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define TIFFTAG_FORWARDMATRIX1 50964
|
||||
#define TIFFTAG_FORWARDMATRIX2 50965
|
||||
|
||||
static const float colormatrix_srgb[] = { 3.2409, -1.5373, -0.4986, -0.9692, 1.8759,
|
||||
0.0415, 0.0556, -0.2039, 1.0569 };
|
||||
static const float colormatrix_srgb[] = { 3.2409f, -1.5373f, -0.4986f,
|
||||
-0.9692f, 1.8759f, 0.0415f,
|
||||
0.0556f, -0.2039f, 1.0569f };
|
||||
|
||||
static MPPipeline *pipeline;
|
||||
|
||||
@@ -28,10 +32,10 @@ static volatile bool is_capturing = false;
|
||||
static volatile int frames_processed = 0;
|
||||
static volatile int frames_received = 0;
|
||||
|
||||
static const struct mp_camera_config *camera;
|
||||
static int camera_rotation;
|
||||
libmegapixels_camera *pr_camera;
|
||||
libmegapixels_mode *mode;
|
||||
|
||||
static MPMode mode;
|
||||
static int camera_rotation;
|
||||
|
||||
static int burst_length;
|
||||
static int captures_remaining = 0;
|
||||
@@ -281,17 +285,19 @@ mp_process_pipeline_buffer_get_texture_id(MPProcessPipelineBuffer *buf)
|
||||
}
|
||||
|
||||
static void
|
||||
repack_image_sequencial(const uint8_t *src_buf, uint8_t *dst_buf, MPMode *mode)
|
||||
repack_image_sequencial(const uint8_t *src_buf,
|
||||
uint8_t *dst_buf,
|
||||
libmegapixels_mode *mode)
|
||||
{
|
||||
uint16_t pixels[4];
|
||||
uint32_t row_length =
|
||||
mp_pixel_format_width_to_bytes(mode->pixel_format, mode->width);
|
||||
libmegapixels_mode_width_to_bytes(mode->format, mode->width);
|
||||
uint32_t padding_bytes =
|
||||
mp_pixel_format_width_to_padding(mode->pixel_format, mode->width);
|
||||
libmegapixels_mode_width_to_padding(mode->format, mode->width);
|
||||
size_t si = 0;
|
||||
|
||||
// Image data must be 10-bit packed
|
||||
assert(mp_pixel_format_bits_per_pixel(mode->pixel_format) == 10);
|
||||
assert(libmegapixels_format_bits_per_pixel(mode->format) == 10);
|
||||
|
||||
/*
|
||||
* Repack 40 bits stored in sensor format into sequencial format
|
||||
@@ -439,10 +445,10 @@ process_image_for_preview(const uint8_t *image)
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_LUMINANCE,
|
||||
mp_pixel_format_width_to_bytes(mode.pixel_format, mode.width) +
|
||||
mp_pixel_format_width_to_padding(mode.pixel_format,
|
||||
mode.width),
|
||||
mode.height,
|
||||
libmegapixels_mode_width_to_bytes(mode->format, mode->width) +
|
||||
libmegapixels_mode_width_to_padding(mode->format,
|
||||
mode->width),
|
||||
mode->height,
|
||||
0,
|
||||
GL_LUMINANCE,
|
||||
GL_UNSIGNED_BYTE,
|
||||
@@ -535,26 +541,26 @@ process_image_for_capture(const uint8_t *image, int count)
|
||||
|
||||
// Define TIFF thumbnail
|
||||
TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 1);
|
||||
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, mode.width >> 4);
|
||||
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, mode.height >> 4);
|
||||
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, mode->width >> 4);
|
||||
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, mode->height >> 4);
|
||||
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
|
||||
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
|
||||
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
|
||||
TIFFSetField(tif, TIFFTAG_MAKE, mp_get_device_make());
|
||||
TIFFSetField(tif, TIFFTAG_MODEL, mp_get_device_model());
|
||||
TIFFSetField(tif, TIFFTAG_MAKE, "MAKE"); // TODO: fix
|
||||
TIFFSetField(tif, TIFFTAG_MODEL, "MODEL");
|
||||
uint16_t orientation;
|
||||
if (camera_rotation == 0) {
|
||||
orientation = camera->mirrored ? ORIENTATION_TOPRIGHT :
|
||||
ORIENTATION_TOPLEFT;
|
||||
orientation =
|
||||
mode->mirrored ? ORIENTATION_TOPRIGHT : ORIENTATION_TOPLEFT;
|
||||
} else if (camera_rotation == 90) {
|
||||
orientation = camera->mirrored ? ORIENTATION_RIGHTBOT :
|
||||
ORIENTATION_LEFTBOT;
|
||||
orientation =
|
||||
mode->mirrored ? ORIENTATION_RIGHTBOT : ORIENTATION_LEFTBOT;
|
||||
} else if (camera_rotation == 180) {
|
||||
orientation = camera->mirrored ? ORIENTATION_BOTLEFT :
|
||||
ORIENTATION_BOTRIGHT;
|
||||
orientation =
|
||||
mode->mirrored ? ORIENTATION_BOTLEFT : ORIENTATION_BOTRIGHT;
|
||||
} else {
|
||||
orientation = camera->mirrored ? ORIENTATION_LEFTTOP :
|
||||
ORIENTATION_RIGHTTOP;
|
||||
orientation =
|
||||
mode->mirrored ? ORIENTATION_LEFTTOP : ORIENTATION_RIGHTTOP;
|
||||
}
|
||||
TIFFSetField(tif, TIFFTAG_ORIENTATION, orientation);
|
||||
TIFFSetField(tif, TIFFTAG_DATETIME, datetime);
|
||||
@@ -568,96 +574,98 @@ process_image_for_capture(const uint8_t *image, int count)
|
||||
char uniquecameramodel[255];
|
||||
sprintf(uniquecameramodel,
|
||||
"%s %s",
|
||||
mp_get_device_make(),
|
||||
mp_get_device_model());
|
||||
"MAKE", // TODO: fix
|
||||
"MODEL");
|
||||
TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, uniquecameramodel);
|
||||
|
||||
// Color matrices
|
||||
if (camera->calibration.color_matrix_1[0]) {
|
||||
/*
|
||||
if (pr_camera->calibration.color_matrix_1[0]) {
|
||||
TIFFSetField(tif,
|
||||
TIFFTAG_COLORMATRIX1,
|
||||
9,
|
||||
camera->calibration.color_matrix_1);
|
||||
} else if (camera->colormatrix[0]) {
|
||||
TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, camera->colormatrix);
|
||||
pr_camera->calibration.color_matrix_1);
|
||||
} else if (pr_camera->colormatrix[0]) {
|
||||
TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, pr_camera->colormatrix);
|
||||
} else {
|
||||
TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colormatrix_srgb);
|
||||
}
|
||||
if (camera->calibration.color_matrix_2[0]) {
|
||||
if (pr_camera->calibration.color_matrix_2[0]) {
|
||||
TIFFSetField(tif,
|
||||
TIFFTAG_COLORMATRIX2,
|
||||
9,
|
||||
camera->calibration.color_matrix_2);
|
||||
pr_camera->calibration.color_matrix_2);
|
||||
}
|
||||
|
||||
if (camera->calibration.forward_matrix_1[0]) {
|
||||
if (pr_camera->calibration.forward_matrix_1[0]) {
|
||||
TIFFSetField(tif,
|
||||
TIFFTAG_FORWARDMATRIX1,
|
||||
9,
|
||||
camera->calibration.forward_matrix_1);
|
||||
} else if (camera->forwardmatrix[0]) {
|
||||
TIFFSetField(tif, TIFFTAG_FORWARDMATRIX1, 9, camera->forwardmatrix);
|
||||
pr_camera->calibration.forward_matrix_1);
|
||||
} else if (pr_camera->forwardmatrix[0]) {
|
||||
TIFFSetField(tif, TIFFTAG_FORWARDMATRIX1, 9,
|
||||
pr_camera->forwardmatrix);
|
||||
}
|
||||
|
||||
if (camera->calibration.forward_matrix_2[0]) {
|
||||
if (pr_camera->calibration.forward_matrix_2[0]) {
|
||||
TIFFSetField(tif,
|
||||
TIFFTAG_FORWARDMATRIX2,
|
||||
9,
|
||||
camera->calibration.forward_matrix_2);
|
||||
pr_camera->calibration.forward_matrix_2);
|
||||
}
|
||||
|
||||
static const float neutral[] = { 1.0, 1.0, 1.0 };
|
||||
TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
|
||||
if (camera->calibration.illuminant_1) {
|
||||
if (pr_camera->calibration.illuminant_1) {
|
||||
TIFFSetField(tif,
|
||||
TIFFTAG_CALIBRATIONILLUMINANT1,
|
||||
camera->calibration.illuminant_1);
|
||||
pr_camera->calibration.illuminant_1);
|
||||
} else {
|
||||
TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21);
|
||||
}
|
||||
|
||||
if (camera->calibration.illuminant_2) {
|
||||
if (pr_camera->calibration.illuminant_2) {
|
||||
TIFFSetField(tif,
|
||||
TIFFTAG_CALIBRATIONILLUMINANT2,
|
||||
camera->calibration.illuminant_2);
|
||||
pr_camera->calibration.illuminant_2);
|
||||
}
|
||||
|
||||
if (camera->calibration.tone_curve_length) {
|
||||
if (pr_camera->calibration.tone_curve_length) {
|
||||
TIFFSetField(tif,
|
||||
DCPTAG_PROFILE_TONE_CURVE,
|
||||
camera->calibration.tone_curve_length,
|
||||
camera->calibration.tone_curve);
|
||||
pr_camera->calibration.tone_curve_length,
|
||||
pr_camera->calibration.tone_curve);
|
||||
}
|
||||
|
||||
if (camera->calibration.hue_sat_map_dims[0]) {
|
||||
if (pr_camera->calibration.hue_sat_map_dims[0]) {
|
||||
TIFFSetField(tif,
|
||||
DCPTAG_PROFILE_HUE_SAT_MAP_DIMS,
|
||||
3,
|
||||
camera->calibration.hue_sat_map_dims);
|
||||
pr_camera->calibration.hue_sat_map_dims);
|
||||
TIFFSetField(tif,
|
||||
DCPTAG_PROFILE_HUE_SAT_MAP_DATA_1,
|
||||
camera->calibration.hue_sat_map_dims[0] *
|
||||
camera->calibration.hue_sat_map_dims[1] *
|
||||
camera->calibration.hue_sat_map_dims[2] * 3,
|
||||
camera->calibration.hue_sat_map_data_1);
|
||||
if (camera->calibration.hue_sat_map_data_2 != NULL) {
|
||||
pr_camera->calibration.hue_sat_map_dims[0] *
|
||||
pr_camera->calibration.hue_sat_map_dims[1] *
|
||||
pr_camera->calibration.hue_sat_map_dims[2] * 3,
|
||||
pr_camera->calibration.hue_sat_map_data_1);
|
||||
if (pr_camera->calibration.hue_sat_map_data_2 != NULL) {
|
||||
TIFFSetField(
|
||||
tif,
|
||||
DCPTAG_PROFILE_HUE_SAT_MAP_DATA_2,
|
||||
camera->calibration.hue_sat_map_dims[0] *
|
||||
camera->calibration.hue_sat_map_dims[1] *
|
||||
camera->calibration.hue_sat_map_dims[2] * 3,
|
||||
camera->calibration.hue_sat_map_data_2);
|
||||
pr_camera->calibration.hue_sat_map_dims[0] *
|
||||
pr_camera->calibration.hue_sat_map_dims[1] *
|
||||
pr_camera->calibration.hue_sat_map_dims[2] *
|
||||
3, pr_camera->calibration.hue_sat_map_data_2);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
TIFFSetField(tif, TIFFTAG_ANALOGBALANCE, 3, balance);
|
||||
|
||||
// Write black thumbnail, only windows uses this
|
||||
{
|
||||
unsigned char *buf =
|
||||
(unsigned char *)calloc(1, (mode.width >> 4) * 3);
|
||||
for (int row = 0; row < (mode.height >> 4); row++) {
|
||||
(unsigned char *)calloc(1, (mode->width >> 4) * 3);
|
||||
for (int row = 0; row < (mode->height >> 4); row++) {
|
||||
TIFFWriteScanline(tif, buf, row, 0);
|
||||
}
|
||||
free(buf);
|
||||
@@ -666,11 +674,11 @@ process_image_for_capture(const uint8_t *image, int count)
|
||||
|
||||
// Define main photo
|
||||
TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);
|
||||
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, mode.width);
|
||||
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, mode.height);
|
||||
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, mode->width);
|
||||
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, mode->height);
|
||||
TIFFSetField(tif,
|
||||
TIFFTAG_BITSPERSAMPLE,
|
||||
mp_pixel_format_bits_per_pixel(mode.pixel_format));
|
||||
libmegapixels_format_bits_per_pixel(mode->format));
|
||||
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
|
||||
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
|
||||
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||
@@ -684,39 +692,31 @@ process_image_for_capture(const uint8_t *image, int count)
|
||||
TIFFSetField(tif,
|
||||
TIFFTAG_CFAPATTERN,
|
||||
4,
|
||||
mp_pixel_format_cfa_pattern(mode.pixel_format));
|
||||
libmegapixels_format_cfa_pattern(mode->format));
|
||||
#endif
|
||||
printf("TIFF version %d\n", TIFFLIB_VERSION);
|
||||
int whitelevel = camera->whitelevel;
|
||||
if (!whitelevel) {
|
||||
whitelevel =
|
||||
(1 << mp_pixel_format_pixel_depth(mode.pixel_format)) - 1;
|
||||
}
|
||||
int whitelevel =
|
||||
(1 << libmegapixels_format_bits_per_pixel(mode->format)) - 1;
|
||||
TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &whitelevel);
|
||||
if (camera->blacklevel) {
|
||||
const float blacklevel = camera->blacklevel;
|
||||
TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 1, &blacklevel);
|
||||
}
|
||||
TIFFCheckpointDirectory(tif);
|
||||
printf("Writing frame to %s\n", fname);
|
||||
|
||||
uint8_t *output_image = (uint8_t *)image;
|
||||
|
||||
// Repack 10-bit image from sensor format into a sequencial format
|
||||
if (mp_pixel_format_bits_per_pixel(mode.pixel_format) == 10) {
|
||||
output_image = malloc(mp_pixel_format_width_to_bytes(
|
||||
mode.pixel_format, mode.width) *
|
||||
mode.height);
|
||||
if (libmegapixels_format_bits_per_pixel(mode->format) == 10) {
|
||||
output_image = malloc(libmegapixels_mode_width_to_bytes(
|
||||
mode->format, mode->width) *
|
||||
mode->height);
|
||||
|
||||
repack_image_sequencial(image, output_image, &mode);
|
||||
repack_image_sequencial(image, output_image, mode);
|
||||
}
|
||||
|
||||
for (int row = 0; row < mode.height; row++) {
|
||||
TIFFWriteScanline(
|
||||
tif,
|
||||
for (int row = 0; row < mode->height; row++) {
|
||||
TIFFWriteScanline(tif,
|
||||
(void *)output_image +
|
||||
(row * mp_pixel_format_width_to_bytes(
|
||||
mode.pixel_format, mode.width)),
|
||||
(row * libmegapixels_mode_width_to_bytes(
|
||||
mode->format, mode->width)),
|
||||
row,
|
||||
0);
|
||||
}
|
||||
@@ -734,17 +734,20 @@ process_image_for_capture(const uint8_t *image, int count)
|
||||
TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
TIFFSetField(tif,
|
||||
EXIFTAG_EXPOSURETIME,
|
||||
(mode.frame_interval.numerator /
|
||||
(float)mode.frame_interval.denominator) /
|
||||
((float)mode.height / (float)exposure));
|
||||
if (camera->iso_min && camera->iso_max) {
|
||||
|
||||
if (pr_camera->iso_min && pr_camera->iso_max) {
|
||||
uint16_t isospeed = remap(
|
||||
gain - 1, 0, gain_max, camera->iso_min, camera->iso_max);
|
||||
TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, &isospeed);
|
||||
gain - 1, 0, gain_max, pr_camera->iso_min,
|
||||
pr_camera->iso_max); TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1,
|
||||
&isospeed);
|
||||
}
|
||||
if (!camera->has_flash) {
|
||||
if (!pr_camera->has_flash) {
|
||||
// No flash function
|
||||
TIFFSetField(tif, EXIFTAG_FLASH, 0x20);
|
||||
} else if (flash_enabled) {
|
||||
@@ -754,20 +757,24 @@ process_image_for_capture(const uint8_t *image, int count)
|
||||
// Flash present but not fired
|
||||
TIFFSetField(tif, EXIFTAG_FLASH, 0x0);
|
||||
}
|
||||
*/
|
||||
|
||||
TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, datetime);
|
||||
TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, datetime);
|
||||
if (camera->fnumber) {
|
||||
TIFFSetField(tif, EXIFTAG_FNUMBER, camera->fnumber);
|
||||
/*
|
||||
if (pr_camera->fnumber) {
|
||||
TIFFSetField(tif, EXIFTAG_FNUMBER, pr_camera->fnumber);
|
||||
}
|
||||
if (camera->focallength) {
|
||||
TIFFSetField(tif, EXIFTAG_FOCALLENGTH, camera->focallength);
|
||||
if (pr_camera->focallength) {
|
||||
TIFFSetField(tif, EXIFTAG_FOCALLENGTH, pr_camera->focallength);
|
||||
}
|
||||
if (camera->focallength && camera->cropfactor) {
|
||||
if (pr_camera->focallength && pr_camera->cropfactor) {
|
||||
TIFFSetField(tif,
|
||||
EXIFTAG_FOCALLENGTHIN35MMFILM,
|
||||
(short)(camera->focallength * camera->cropfactor));
|
||||
(short)(pr_camera->focallength *
|
||||
pr_camera->cropfactor));
|
||||
}
|
||||
*/
|
||||
uint64_t exif_offset = 0;
|
||||
TIFFWriteCustomDirectory(tif, &exif_offset);
|
||||
TIFFFreeDirectory(tif);
|
||||
@@ -870,19 +877,19 @@ process_image(MPPipeline *pipeline, const MPBuffer *buffer)
|
||||
#endif
|
||||
|
||||
size_t size =
|
||||
(mp_pixel_format_width_to_bytes(mode.pixel_format, mode.width) +
|
||||
mp_pixel_format_width_to_padding(mode.pixel_format, mode.width)) *
|
||||
mode.height;
|
||||
(libmegapixels_mode_width_to_bytes(mode->format, mode->width) +
|
||||
libmegapixels_mode_width_to_padding(mode->format, mode->width)) *
|
||||
mode->height;
|
||||
uint8_t *image = malloc(size);
|
||||
memcpy(image, buffer->data, size);
|
||||
mp_io_pipeline_release_buffer(buffer->index);
|
||||
|
||||
MPZBarImage *zbar_image = mp_zbar_image_new(image,
|
||||
mode.pixel_format,
|
||||
mode.width,
|
||||
mode.height,
|
||||
mode->format,
|
||||
mode->width,
|
||||
mode->height,
|
||||
camera_rotation,
|
||||
camera->mirrored);
|
||||
mode->mirrored);
|
||||
mp_zbar_pipeline_process_image(mp_zbar_image_ref(zbar_image));
|
||||
|
||||
#ifdef PROFILE_PROCESS
|
||||
@@ -968,10 +975,10 @@ mp_process_pipeline_capture()
|
||||
static void
|
||||
on_output_changed(bool format_changed)
|
||||
{
|
||||
output_buffer_width = mode.width / 2;
|
||||
output_buffer_height = mode.height / 2;
|
||||
output_buffer_width = mode->width / 2;
|
||||
output_buffer_height = mode->height / 2;
|
||||
|
||||
if (camera->rotate != 0 || camera->rotate != 180) {
|
||||
if (mode->rotation != 0 || mode->rotation != 180) {
|
||||
int tmp = output_buffer_width;
|
||||
output_buffer_width = output_buffer_height;
|
||||
output_buffer_height = tmp;
|
||||
@@ -997,22 +1004,21 @@ on_output_changed(bool format_changed)
|
||||
if (gles2_debayer)
|
||||
gles2_debayer_free(gles2_debayer);
|
||||
|
||||
gles2_debayer = gles2_debayer_new(mode.pixel_format);
|
||||
gles2_debayer = gles2_debayer_new(mode->format);
|
||||
check_gl();
|
||||
|
||||
gles2_debayer_use(gles2_debayer);
|
||||
}
|
||||
|
||||
gles2_debayer_configure(
|
||||
gles2_debayer,
|
||||
gles2_debayer_configure(gles2_debayer,
|
||||
output_buffer_width,
|
||||
output_buffer_height,
|
||||
mode.width,
|
||||
mode.height,
|
||||
camera->rotate,
|
||||
camera->mirrored,
|
||||
camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
|
||||
camera->blacklevel);
|
||||
mode->width,
|
||||
mode->height,
|
||||
mode->rotation,
|
||||
0,
|
||||
NULL,
|
||||
0);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1025,15 +1031,24 @@ mod(int a, int b)
|
||||
static void
|
||||
update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state)
|
||||
{
|
||||
const bool output_changed = !mp_mode_is_equivalent(&mode, &state->mode) ||
|
||||
const bool output_changed =
|
||||
!libmegapixels_mode_equals(mode, state->camera->current_mode) ||
|
||||
preview_width != state->preview_width ||
|
||||
preview_height != state->preview_height ||
|
||||
device_rotation != state->device_rotation;
|
||||
|
||||
const bool format_changed = mode.pixel_format != state->mode.pixel_format;
|
||||
bool format_changed = mode == NULL;
|
||||
|
||||
camera = state->camera;
|
||||
mode = state->mode;
|
||||
if (!format_changed &&
|
||||
mode->v4l_pixfmt != state->camera->current_mode->v4l_pixfmt) {
|
||||
format_changed = true;
|
||||
}
|
||||
if (mode == NULL) {
|
||||
mode = state->camera->current_mode;
|
||||
}
|
||||
|
||||
pr_camera = state->camera;
|
||||
mode = state->camera->current_mode;
|
||||
|
||||
preview_width = state->preview_width;
|
||||
preview_height = state->preview_height;
|
||||
@@ -1052,16 +1067,14 @@ update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state
|
||||
exposure = state->exposure;
|
||||
|
||||
if (output_changed) {
|
||||
camera_rotation = mod(camera->rotate - device_rotation, 360);
|
||||
camera_rotation = mod(mode->rotation - device_rotation, 360);
|
||||
|
||||
on_output_changed(format_changed);
|
||||
}
|
||||
|
||||
struct mp_main_state main_state = {
|
||||
.camera = camera,
|
||||
.camera = pr_camera,
|
||||
.mode = mode,
|
||||
.image_width = output_buffer_width,
|
||||
.image_height = output_buffer_height,
|
||||
.gain_is_manual = state->gain_is_manual,
|
||||
.gain = gain,
|
||||
.gain_max = gain_max,
|
||||
|
@@ -1,14 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "camera.h"
|
||||
#include "camera_config.h"
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
typedef struct _GdkSurface GdkSurface;
|
||||
|
||||
struct mp_process_pipeline_state {
|
||||
const struct mp_camera_config *camera;
|
||||
MPMode mode;
|
||||
libmegapixels_camera *camera;
|
||||
|
||||
int burst_length;
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
|
||||
struct _MPZBarImage {
|
||||
uint8_t *data;
|
||||
MPPixelFormat pixel_format;
|
||||
int format;
|
||||
int width;
|
||||
int height;
|
||||
int rotation;
|
||||
@@ -171,15 +171,6 @@ process_image(MPPipeline *pipeline, MPZBarImage **_image)
|
||||
{
|
||||
MPZBarImage *image = *_image;
|
||||
|
||||
assert(image->pixel_format == MP_PIXEL_FMT_BGGR8 ||
|
||||
image->pixel_format == MP_PIXEL_FMT_GBRG8 ||
|
||||
image->pixel_format == MP_PIXEL_FMT_GRBG8 ||
|
||||
image->pixel_format == MP_PIXEL_FMT_RGGB8 ||
|
||||
image->pixel_format == MP_PIXEL_FMT_BGGR10P ||
|
||||
image->pixel_format == MP_PIXEL_FMT_GBRG10P ||
|
||||
image->pixel_format == MP_PIXEL_FMT_GRBG10P ||
|
||||
image->pixel_format == MP_PIXEL_FMT_RGGB10P);
|
||||
|
||||
// Create a grayscale image for scanning from the current preview.
|
||||
// Rotate/mirror correctly.
|
||||
int width = image->width / 2;
|
||||
@@ -187,11 +178,12 @@ process_image(MPPipeline *pipeline, MPZBarImage **_image)
|
||||
|
||||
uint8_t *data = malloc(width * height * sizeof(uint8_t));
|
||||
size_t row_length =
|
||||
mp_pixel_format_width_to_bytes(image->pixel_format, image->width);
|
||||
libmegapixels_mode_width_to_bytes(image->format, image->width);
|
||||
int padding_bytes =
|
||||
mp_pixel_format_width_to_padding(image->pixel_format, image->width);
|
||||
libmegapixels_mode_width_to_padding(image->format, image->width);
|
||||
size_t i = 0, padding_offset = 0;
|
||||
size_t offset;
|
||||
/* TODO: implement
|
||||
switch (image->pixel_format) {
|
||||
case MP_PIXEL_FMT_BGGR8:
|
||||
case MP_PIXEL_FMT_GBRG8:
|
||||
@@ -267,6 +259,7 @@ process_image(MPPipeline *pipeline, MPZBarImage **_image)
|
||||
mp_zbar_image_unref(image);
|
||||
|
||||
++frames_processed;
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
@@ -288,7 +281,7 @@ mp_zbar_pipeline_process_image(MPZBarImage *image)
|
||||
|
||||
MPZBarImage *
|
||||
mp_zbar_image_new(uint8_t *data,
|
||||
MPPixelFormat pixel_format,
|
||||
int format,
|
||||
int width,
|
||||
int height,
|
||||
int rotation,
|
||||
@@ -296,7 +289,7 @@ mp_zbar_image_new(uint8_t *data,
|
||||
{
|
||||
MPZBarImage *image = malloc(sizeof(MPZBarImage));
|
||||
image->data = data;
|
||||
image->pixel_format = pixel_format;
|
||||
image->format = format;
|
||||
image->width = width;
|
||||
image->height = height;
|
||||
image->rotation = rotation;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "camera_config.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct _MPZBarImage MPZBarImage;
|
||||
|
||||
@@ -22,7 +23,7 @@ void mp_zbar_pipeline_stop();
|
||||
void mp_zbar_pipeline_process_image(MPZBarImage *image);
|
||||
|
||||
MPZBarImage *mp_zbar_image_new(uint8_t *data,
|
||||
MPPixelFormat pixel_format,
|
||||
int format,
|
||||
int width,
|
||||
int height,
|
||||
int rotation,
|
||||
|
Reference in New Issue
Block a user