diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2011-07-30 14:33:37 +0200 |
---|---|---|
committer | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2011-07-30 14:33:37 +0200 |
commit | febcb53ca85d911619456c09c4be49fd73c4964b (patch) | |
tree | 12ae3a93d117b56da6e1213882f5cc6de3977adb /snapshot.c |
omap3-isp-live: Initial commit
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'snapshot.c')
-rw-r--r-- | snapshot.c | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/snapshot.c b/snapshot.c new file mode 100644 index 0000000..5728ae6 --- /dev/null +++ b/snapshot.c @@ -0,0 +1,418 @@ +/* + * OMAP3 ISP snapshot test application + * + * Copyright (C) 2010-2011 Ideas on board SPRL + * + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * 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 <endian.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/select.h> +#include <sys/time.h> + +#include <linux/spi/spidev.h> + +#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; +} |