/* * OMAP3 ISP snapshot test application * * Copyright (C) 2010-2011 Ideas on board SPRL * * Contact: Laurent Pinchart * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define _BSD_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "isp/list.h" #include "isp/omap3isp.h" #include "isp/tools.h" #include "isp/v4l2.h" #include "isp/v4l2-pool.h" #define MEDIA_DEVICE "/dev/media0" #define SELECT_TIMEOUT 2000 /* in milliseconds */ #define VIEWFINDER_WIDTH 1024 #define VIEWFINDER_HEIGHT 768 #define SNAPSHOT_WIDTH 2048 #define SNAPSHOT_HEIGHT 1536 static unsigned int frame_count = 0; static bool done = false; static unsigned int snapshot_interval = 20; static struct timespec snapshot_time; /* ----------------------------------------------------------------------------- * Events */ struct event_fd { struct list_entry list; int fd; enum omap3_isp_event_type type; void (*callback)(void *priv); void *priv; }; static struct { struct list_entry events; int maxfd; fd_set rfds; fd_set wfds; } events; static void events_watch_fd(int fd, enum omap3_isp_event_type type, void(*callback)(void *), void *priv) { struct event_fd *event; event = malloc(sizeof *event); if (event == NULL) return; event->fd = fd; event->type = type; event->callback = callback; event->priv = priv; if (event->type == OMAP3_ISP_EVENT_READ) FD_SET(fd, &events.rfds); else FD_SET(fd, &events.wfds); events.maxfd = max(events.maxfd, fd); list_append(&event->list, &events.events); } static void events_unwatch_fd(int fd) { struct event_fd *event = NULL; struct event_fd *entry; int maxfd = 0; list_for_each_entry(entry, &events.events, list) { if (entry->fd == fd) event = entry; else maxfd = max(maxfd, entry->fd); } if (event == NULL) return; if (event->type == OMAP3_ISP_EVENT_READ) FD_CLR(fd, &events.rfds); else FD_CLR(fd, &events.wfds); events.maxfd = maxfd; list_remove(&event->list); free(event); } static void events_init(void) { memset(&events, 0, sizeof events); FD_ZERO(&events.rfds); FD_ZERO(&events.wfds); events.maxfd = 0; list_init(&events.events); } static void events_dispatch(const fd_set *rfds, const fd_set *wfds) { struct event_fd *event; list_for_each_entry(event, &events.events, list) { if (event->type == OMAP3_ISP_EVENT_READ && FD_ISSET(event->fd, rfds)) event->callback(event->priv); if (event->type == OMAP3_ISP_EVENT_WRITE && FD_ISSET(event->fd, wfds)) event->callback(event->priv); } } /* -------------------------------------------------------------------------- */ static void sigint_handler(int signal __attribute__((__unused__))) { /* Set the done flag to true when the user presses CTRL-C to interrupt * the main loop. */ done = true; } static void snapshot_process(struct omap3_isp_device *isp, struct v4l2_video_buffer *buffer) { static struct timespec ts; int ret; if (buffer->error) { printf("warning: error in dequeued buffer, skipping\n"); return; } ts.tv_sec = buffer->timestamp.tv_sec - snapshot_time.tv_sec; ts.tv_nsec = (buffer->timestamp.tv_usec * 1000) - snapshot_time.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_sec--; ts.tv_nsec += 1000000000; } printf("snapshot captured in %lu.%06lus.\n", ts.tv_sec, ts.tv_nsec / 1000); /* Requeue the buffer */ ret = omap3_isp_snapshot_put_buffer(isp, buffer); if (ret < 0) { printf("error: unable to requeue buffer: %s (%d)\n", strerror(-ret), ret); return; } } static void viewfinder_process(struct omap3_isp_device *isp, struct v4l2_video_buffer *buffer) { int ret; if (buffer->error) { printf("warning: error in dequeued buffer, skipping\n"); return; } frame_count++; printf("viewfinder frame %u captured.\n", frame_count); if ((frame_count % snapshot_interval) == 0) { clock_gettime(CLOCK_MONOTONIC, &snapshot_time); omap3_isp_snapshot_capture(isp); } /* Requeue the buffer */ ret = omap3_isp_viewfinder_put_buffer(isp, buffer); if (ret < 0) { printf("error: unable to requeue buffer: %s (%d)\n", strerror(-ret), ret); return; } } static void usage(const char *argv0) { printf("Usage: %s [options]\n", argv0); printf("Supported options:\n"); printf("-h, --help Show this help screen\n"); printf("-s, --snap n Capture a snapshot every n frames\n"); printf("-y, --yuv YUV snapshots\n"); } static struct option opts[] = { { "help", 0, 0, 'h' }, { "snap", 0, 0, 's' }, { "yuv", 0, 0, 'y' }, { 0, 0, 0, 0 } }; int main(int argc __attribute__((__unused__)), char *argv[] __attribute__((__unused__))) { enum v4l2_mbus_pixelcode snap_code = V4L2_MBUS_FMT_SGRBG10_1X10; struct v4l2_mbus_framefmt view_format; struct v4l2_mbus_framefmt snap_format; struct v4l2_buffers_pool *pool = NULL; struct omap3_isp_device *isp = NULL; struct omap3_isp_operations ops; struct timespec start, end; unsigned int count = 0; int exit_code = EXIT_FAILURE; float fps; int ret; int c; while ((c = getopt_long(argc, argv, "hs:y", opts, NULL)) != -1) { switch (c) { case 'h': usage(argv[0]); return 0; case 's': snapshot_interval = strtoul(optarg, NULL, 10); if (snapshot_interval == 0) { printf("snapshot interval value must be >0.\n"); return 1; } break; case 'y': snap_code = V4L2_MBUS_FMT_YUYV8_1X16; break; default: printf("Invalid option -%c\n", c); printf("Run %s -h for help.\n", argv[0]); return 1; } } events_init(); /* Register a signal handler for SIGINT, received when the user presses * CTRL-C. This will allow the main loop to be interrupted, and resources * to be freed cleanly. */ signal(SIGINT, sigint_handler); /* Open the OMAP3 ISP device and setup the capture pipeline. */ memset(&ops, 0, sizeof ops); ops.viewfinder_ready = viewfinder_process; ops.snapshot_ready = snapshot_process; ops.watch_fd = events_watch_fd; ops.unwatch_fd = events_unwatch_fd; isp = omap3_isp_open(MEDIA_DEVICE, &ops); if (isp == NULL) { printf("error: unable to open media device %s\n", MEDIA_DEVICE); goto cleanup; } memset(&view_format, 0, sizeof view_format); view_format.code = V4L2_MBUS_FMT_YUYV8_1X16; view_format.width = VIEWFINDER_WIDTH; view_format.height = VIEWFINDER_HEIGHT; ret = omap3_isp_viewfinder_setup(isp, &view_format); if (ret < 0) { printf("error: unable to setup pipeline\n"); goto cleanup; } printf("viewfinder configured for %04x %ux%u\n", view_format.code, view_format.width, view_format.height); memset(&snap_format, 0, sizeof snap_format); snap_format.code = snap_code; snap_format.width = SNAPSHOT_WIDTH; snap_format.height = SNAPSHOT_HEIGHT; ret = omap3_isp_snapshot_setup(isp, &snap_format); if (ret < 0) { printf("error: unable to setup pipeline\n"); goto cleanup; } printf("snapshot configured for %04x %ux%u\n", snap_format.code, snap_format.width, snap_format.height); /* Allocate a buffers pool and use it for the viewfinder. */ pool = v4l2_buffers_pool_new(4); if (pool == NULL) { printf("error: unable to create buffers pool\n"); goto cleanup; } ret = v4l2_buffers_pool_alloc(pool, view_format.width * view_format.height * 2, 4096); if (ret < 0) { printf("error: unable to allocate buffers memory\n"); goto cleanup; } ret = omap3_isp_viewfinder_set_pool(isp, pool); if (ret < 0) { printf("error: unable to set buffers pool\n"); goto cleanup; } /* Start the ISP. */ ret = omap3_isp_viewfinder_start(isp); if (ret < 0) goto cleanup; clock_gettime(CLOCK_MONOTONIC, &start); /* Main capture loop. Wait for a video buffer using select() and process * it. */ while (!done) { struct timeval timeout; fd_set rfds; fd_set wfds; timeout.tv_sec = SELECT_TIMEOUT / 1000; timeout.tv_usec = (SELECT_TIMEOUT % 1000) * 1000; rfds = events.rfds; wfds = events.wfds; ret = select(events.maxfd + 1, &rfds, &wfds, NULL, &timeout); if (ret < 0) { /* EINTR means that a signal has been received, continue * to the next iteration in that case. */ if (errno == EINTR) continue; printf("error: select failed with %d\n", errno); goto cleanup; } if (ret == 0) { /* select() should never time out as the ISP is supposed * to capture images continuously. A timeout is thus * considered as a fatal error. */ printf("error: select timeout\n"); goto cleanup; } events_dispatch(&rfds, &wfds); count++; } clock_gettime(CLOCK_MONOTONIC, &end); /* Stop the ISP. */ omap3_isp_viewfinder_stop(isp); /* Print some statistics. */ end.tv_sec -= start.tv_sec; end.tv_nsec -= start.tv_nsec; if (end.tv_nsec < 0) { end.tv_sec--; end.tv_nsec += 1000000000; } fps = count / (end.tv_sec + end.tv_nsec / 1000000000.0); printf("%u images processed in %lu.%06lu seconds (%f fps)\n", count, end.tv_sec, end.tv_nsec / 1000, fps); exit_code = EXIT_SUCCESS; cleanup: /* Cleanup the ISP resources. */ if (isp) omap3_isp_close(isp); if (pool) v4l2_buffers_pool_delete(pool); return exit_code; }