summaryrefslogtreecommitdiff
path: root/live.c
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2011-07-30 14:33:37 +0200
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2011-07-30 14:33:37 +0200
commitfebcb53ca85d911619456c09c4be49fd73c4964b (patch)
tree12ae3a93d117b56da6e1213882f5cc6de3977adb /live.c
omap3-isp-live: Initial commit
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'live.c')
-rw-r--r--live.c467
1 files changed, 467 insertions, 0 deletions
diff --git a/live.c b/live.c
new file mode 100644
index 0000000..bd97036
--- /dev/null
+++ b/live.c
@@ -0,0 +1,467 @@
+/*
+ * OMAP3 live video display 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/fb.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"
+
+#include "videoout.h"
+
+#define MEDIA_DEVICE "/dev/media0"
+#define VIDEOOUT_DEVICE "/dev/video0"
+#define SELECT_TIMEOUT 2000 /* in milliseconds */
+
+static struct timespec fps_ts = { 0, 0 };
+static unsigned int fps_count = 0;
+static bool show_fps = true;
+
+static struct omap3_isp_device *isp = NULL;
+static struct videoout *vo = NULL;
+
+static bool done = false;
+
+/* -----------------------------------------------------------------------------
+ * Events
+ */
+
+struct event_fd {
+ struct list_entry list;
+
+ int fd;
+ bool write;
+ 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, bool write, void(*callback)(void *), void *priv)
+{
+ struct event_fd *event;
+
+ event = malloc(sizeof *event);
+ if (event == NULL)
+ return;
+
+ event->fd = fd;
+ event->write = write;
+ event->callback = callback;
+ event->priv = priv;
+
+ if (write)
+ FD_SET(fd, &events.wfds);
+ else
+ FD_SET(fd, &events.rfds);
+
+ 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->write)
+ FD_CLR(fd, &events.wfds);
+ else
+ FD_CLR(fd, &events.rfds);
+
+ 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->write && FD_ISSET(event->fd, rfds))
+ event->callback(event->priv);
+ if (event->write && FD_ISSET(event->fd, wfds))
+ event->callback(event->priv);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Video input
+ */
+
+static void viewfinder_process(struct omap3_isp_device *isp __attribute__((__unused__)),
+ struct v4l2_video_buffer *buffer)
+{
+ struct timespec ts;
+ float interval;
+
+ if (show_fps){
+ fps_count++;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ interval = ts.tv_sec - fps_ts.tv_sec;
+ interval += (ts.tv_nsec - fps_ts.tv_nsec) / 1000000000.0;
+
+ if (interval >= 5) {
+ if (fps_count > 1)
+ printf("frame rate: %f fps\n", fps_count / interval);
+
+ fps_count = 0;
+ fps_ts = ts;
+ }
+ }
+
+ if (buffer->error) {
+ printf("warning: error in dequeued buffer, skipping\n");
+ return;
+ }
+
+ vo_queue_buffer(vo, buffer);
+}
+
+
+static void events_watch_fd_isp(int fd, enum omap3_isp_event_type type,
+ void (*callback)(void *priv), void *priv)
+{
+ events_watch_fd(fd, type == OMAP3_ISP_EVENT_WRITE, callback, priv);
+}
+
+static struct omap3_isp_operations isp_ops = {
+ .viewfinder_ready = viewfinder_process,
+ .watch_fd = events_watch_fd_isp,
+ .unwatch_fd = events_unwatch_fd,
+};
+
+/* -----------------------------------------------------------------------------
+ * Video output
+ */
+
+static void video_out_process(void *priv __attribute__((__unused__)))
+{
+ struct v4l2_video_buffer buffer;
+ int ret;
+
+ ret = vo_dequeue_buffer(vo, &buffer);
+ if (ret < 0)
+ return;
+
+ /* 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 events_watch_fd_vo(int fd)
+{
+ events_watch_fd(fd, true, video_out_process, NULL);
+}
+
+static struct video_out_operations vo_ops = {
+ .watch_fd = events_watch_fd_vo,
+ .unwatch_fd = events_unwatch_fd,
+};
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer
+ */
+
+static int fb_init(struct v4l2_rect *rect)
+{
+ struct fb_fix_screeninfo fix;
+ struct fb_var_screeninfo var;
+ unsigned int i;
+ void *mem = NULL;
+ int ret;
+ int fd;
+
+ fd = open("/dev/fb0", O_RDWR);
+ if (fd == -1)
+ return -ENODEV;
+
+ ret = ioctl(fd, FBIOGET_FSCREENINFO, &fix);
+ if (ret < 0) {
+ printf("error: failed to get fixed screen info\n");
+ goto done;
+ }
+
+ ret = ioctl(fd, FBIOGET_VSCREENINFO, &var);
+ if (ret < 0) {
+ printf("error: failed to get variable screen info\n");
+ goto done;
+ }
+
+ mem = mmap(NULL, fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0);
+ if (mem == MAP_FAILED) {
+ printf("error: unable to map frame buffer\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ /* Fill the frame buffer with the background color. */
+ for (i = 0; i < fix.smem_len; i += 4)
+ *(uint32_t *)(mem + i) = 0x00123456;
+
+ /* Return the frame buffer size. */
+ rect->left = var.xoffset;
+ rect->top = var.yoffset;
+ rect->width = var.xres;
+ rect->height = var.yres;
+
+done:
+ if (mem != NULL)
+ munmap(mem, fix.smem_len);
+ close(fd);
+
+ return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+
+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 usage(const char *argv0)
+{
+ printf("Usage: %s [options]\n", argv0);
+ printf("Supported options:\n");
+ printf("-h, --help Show this help screen\n");
+}
+
+static struct option opts[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc __attribute__((__unused__)), char *argv[] __attribute__((__unused__)))
+{
+ struct v4l2_mbus_framefmt view_format;
+ struct v4l2_buffers_pool *pool = NULL;
+ struct timespec start, end;
+ unsigned int count = 0;
+ struct v4l2_rect rect;
+ int exit_code = EXIT_FAILURE;
+ float fps;
+ int ret;
+ int c;
+
+ while ((c = getopt_long(argc, argv, "h", opts, NULL)) != -1) {
+ switch (c) {
+ case 'h':
+ usage(argv[0]);
+ return 0;
+ default:
+ printf("Invalid option -%c\n", c);
+ printf("Run %s -h for help.\n", argv[0]);
+ return 1;
+ }
+ }
+
+ events_init();
+
+ memset(&rect, 0, sizeof rect);
+ ret = fb_init(&rect);
+ if (ret < 0) {
+ printf("error: unable to initialize frame buffer\n");
+ goto cleanup;
+ }
+
+ /* 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. */
+ isp = omap3_isp_open(MEDIA_DEVICE, &isp_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 = rect.width;
+ view_format.height = rect.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);
+
+ /* Initialize video output. */
+ vo = vo_init(VIDEOOUT_DEVICE, &vo_ops, rect.width, rect.height);
+ if (vo == NULL) {
+ printf("error: unable to initialize video output\n");
+ goto cleanup;
+ }
+
+ vo_enable_colorkey(vo, 0x123456);
+
+ /* Allocate a buffers pool and use it for the viewfinder. */
+ pool = vo_get_pool(vo);
+ if (pool == NULL) {
+ printf("error: unable to get video output buffers pool\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);
+
+ return exit_code;
+}