@@ -0,0 +1,375 @@
+/*
+ * Samsung S3C/S5P image rotator test application.
+ *
+ * Copyright (c) 2009 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <p.osciak@samsung.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
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <time.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <stdint.h>
+
+#include <linux/fb.h>
+#include <linux/videodev2.h>
+
+#define VIDEO_DEV_NAME "/dev/video"
+#define FB_DEV_NAME "/dev/fb0"
+
+#define NUM_SRC_BUFS 1
+#define NUM_DST_BUFS 1
+
+#define V4L2_CID_PRIVATE_ROTATE (V4L2_CID_PRIVATE_BASE + 0)
+
+#define perror_exit(cond, func)\
+ if (cond) {\
+ fprintf(stderr, "%s:%d: ", __func__, __LINE__);\
+ perror(func);\
+ exit(EXIT_FAILURE);\
+ }
+
+#define error_exit(cond, msg)\
+ if (cond) {\
+ fprintf(stderr, "%s:%d: " msg "\n", __func__, __LINE__);\
+ exit(EXIT_FAILURE);\
+ }
+
+#define perror_ret(cond, func)\
+ if (cond) {\
+ fprintf(stderr, "%s:%d: ", __func__, __LINE__);\
+ perror(func);\
+ return ret;\
+ }
+
+#define memzero(x)\
+ memset(&(x), 0, sizeof (x));
+
+#ifdef _DEBUG
+#define debug(msg, ...)\
+ fprintf(stderr, "%s: " msg, __func__, ##__VA_ARGS__);
+#else
+#define debug(msg, ...)
+#endif
+
+struct buffer {
+ char *addr;
+ unsigned long size;
+ unsigned int index;
+};
+
+static int vid_fd, fb_fd, src_fd, dst_fd;
+static void *fb_addr, *src_addr, *dst_addr;
+static char *in_file, *out_file;
+static int width, height;
+static off_t fb_line_w, fb_buf_w, fb_size;
+static struct fb_var_screeninfo fbinfo;
+static int in_size;
+static int vid_node = 0;
+static int format, framesize, rotation, flip;
+
+static void set_rotation(int angle)
+{
+ struct v4l2_control ctrl;
+ int ret;
+
+ memzero(ctrl);
+ ctrl.id = V4L2_CID_PRIVATE_ROTATE;
+ ctrl.value = angle;
+ ret = ioctl(vid_fd, VIDIOC_S_CTRL, &ctrl);
+ perror_exit(ret != 0, "ioctl");
+}
+
+static void set_flip(int flip)
+{
+ struct v4l2_control ctrl;
+ int ret;
+
+ memzero(ctrl);
+ switch (flip) {
+ case 1:
+ ctrl.id = V4L2_CID_HFLIP;
+ break;
+ case 2:
+ ctrl.id = V4L2_CID_VFLIP;
+ break;
+ default:
+ error_exit(1, "Invalid params\n");
+ }
+
+ ctrl.value = 1;
+
+ ret = ioctl(vid_fd, VIDIOC_S_CTRL, &ctrl);
+ perror_exit(ret != 0, "ioctl");
+}
+
+static void set_fmt(uint32_t format)
+{
+ struct v4l2_format fmt;
+ int ret;
+ long page_size;
+
+ memzero(fmt);
+ switch (format) {
+ case 0:
+ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ framesize = width * height * 2;
+ fb_buf_w = width * 2;
+ break;
+ case 1:
+ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+ framesize = (width * height * 3) / 2;
+ fb_buf_w = width * 3 / 2;
+ break;
+ case 2:
+ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565X;
+ framesize = width * height * 2;
+ fb_buf_w = width * 2;
+ break;
+ case 3:
+ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR32;
+ framesize = width * height * 4;
+ fb_buf_w = width * 4;
+ break;
+ default:
+ error_exit(1, "Invalid params\n");
+ break;
+ }
+
+ page_size = sysconf(_SC_PAGESIZE);
+ framesize = (framesize + page_size - 1) & ~(page_size - 1);
+ debug("Framesize: %d\n", framesize);
+
+ /* Set format for capture */
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = width;
+ fmt.fmt.pix.height = height;
+ fmt.fmt.pix.field = V4L2_FIELD_ANY;
+
+ ret = ioctl(vid_fd, VIDIOC_S_FMT, &fmt);
+ perror_exit(ret != 0, "ioctl");
+
+ /* The same format for output */
+ fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ fmt.fmt.pix.width = width;
+ fmt.fmt.pix.height = height;
+ fmt.fmt.pix.field = V4L2_FIELD_ANY;
+
+ ret = ioctl(vid_fd, VIDIOC_S_FMT, &fmt);
+ perror_exit(ret != 0, "ioctl");
+}
+
+
+static void verify_caps(void)
+{
+ struct v4l2_capability cap;
+ int ret;
+
+ memzero(cap);
+ ret = ioctl(vid_fd, VIDIOC_QUERYCAP, &cap);
+ perror_exit(ret != 0, "ioctl");
+
+ if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
+ error_exit(1, "Device does not support capture\n");
+
+ if (!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT))
+ error_exit(1, "Device does not support output\n");
+
+ if (!(cap.capabilities & V4L2_CAP_STREAMING))
+ error_exit(1, "Device does not support streaming\n");
+}
+
+static void init_video_dev(void)
+{
+ char devname[64];
+
+ snprintf(devname, 64, "%s%d", VIDEO_DEV_NAME, vid_node);
+ vid_fd = open(devname, O_RDWR | O_NONBLOCK, 0);
+ perror_exit(vid_fd < 0, "open");
+
+ verify_caps();
+}
+
+void init_fb(void)
+{
+ int ret;
+
+ fb_fd = open(FB_DEV_NAME, O_RDWR);
+ perror_exit(fb_fd < 0, "open");
+
+ ret = ioctl(fb_fd, FBIOGET_VSCREENINFO, &fbinfo);
+ perror_exit(ret != 0, "ioctl");
+ debug("fbinfo: xres: %d, xres_virt: %d, yres: %d, yres_virt: %d\n",
+ fbinfo.xres, fbinfo.xres_virtual,
+ fbinfo.yres, fbinfo.yres_virtual);
+
+ fb_line_w = fbinfo.xres_virtual * (fbinfo.bits_per_pixel >> 3);
+ debug("fb_buf_w: %d, fb_line_w: %d\n", fb_buf_w, fb_line_w);
+ fb_size = fb_line_w * fbinfo.yres_virtual;
+
+ fb_addr = mmap(0, fb_size, PROT_WRITE | PROT_READ,
+ MAP_SHARED, fb_fd, 0);
+ perror_exit(fb_addr == MAP_FAILED, "mmap");
+}
+
+static void parse_args(int argc, char *argv[])
+{
+ if (argc != 9) {
+ fprintf(stderr, "Usage: "
+ "%s video_node in_file, out_file, format, width, "
+ "height, rotation, flip\n"
+ "rotations: 90, 180, 270;\n"
+ "flip (when rotation==0): 1 - horiz, 2 - vert\n"
+ "formats: 0: 420, 1: 422, 2: 565, 3: 888\n",
+ argv[0]);
+ error_exit(1, "Invalid number of arguments\n");
+ }
+
+ vid_node = atoi(argv[1]);
+ in_file = argv[2];
+ out_file = argv[3];
+ format = atoi(argv[4]);
+ width = atoi(argv[5]);
+ height = atoi(argv[6]);
+ rotation = atoi(argv[7]);
+ flip = atoi(argv[8]);
+}
+
+static void get_buffer(struct buffer *buf, unsigned int i)
+{
+ if ((i+1) * framesize > fb_size)
+ error_exit(1, "Framebuffer memory won't fit in the buffer\n");
+
+ buf->addr = fb_addr + i * framesize;
+ buf->size = framesize;
+}
+
+static void request_buffers(unsigned int *num_src_bufs,
+ unsigned int *num_dst_bufs)
+{
+ struct v4l2_requestbuffers reqbuf;
+ int ret;
+
+ memzero(reqbuf);
+ reqbuf.count = *num_src_bufs;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ reqbuf.memory = V4L2_MEMORY_USERPTR;
+ ret = ioctl(vid_fd, VIDIOC_REQBUFS, &reqbuf);
+ perror_exit(ret != 0, "ioctl");
+ *num_src_bufs = reqbuf.count;
+
+
+ reqbuf.count = *num_dst_bufs;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ret = ioctl(vid_fd, VIDIOC_REQBUFS, &reqbuf);
+ perror_exit(ret != 0, "ioctl");
+ *num_dst_bufs = reqbuf.count;
+}
+
+static int process(struct buffer *src_buf, struct buffer *dst_buf)
+{
+ struct v4l2_buffer buf;
+ enum v4l2_buf_type type;
+ fd_set read_fds;
+ int r;
+ int ret;
+
+ memzero(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ buf.memory = V4L2_MEMORY_USERPTR;
+ buf.index = src_buf->index;
+ buf.m.userptr = (unsigned long)src_buf->addr;
+ buf.length = src_buf->size;
+ type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ret = ioctl(vid_fd, VIDIOC_QBUF, &buf);
+ perror_ret(ret, "ioctl");
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.index = dst_buf->index;
+ buf.m.userptr = (unsigned long)dst_buf->addr;
+ buf.length = dst_buf->size;
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ret = ioctl(vid_fd, VIDIOC_QBUF, &buf);
+ perror_ret(ret, "ioctl");
+
+ ret = ioctl(vid_fd, VIDIOC_STREAMON, &type);
+ perror_ret(ret, "ioctl");
+
+ FD_ZERO(&read_fds);
+ FD_SET(vid_fd, &read_fds);
+ r = select(vid_fd + 1, &read_fds, NULL, NULL, 0);
+ perror_ret(ret, "ioctl");
+
+ ret = ioctl(vid_fd, VIDIOC_STREAMOFF, &type);
+ perror_ret(ret, "ioctl");
+
+ return 0;
+}
+
+/* Usage: video_node in_file, out_file, format, width, height, rotation, flip
+ * rotations: 90, 180, 270; flip (when rotation==0): 1 - horiz, 2 - vert
+ * formats: 0: 420, 1: 422, 2: 565, 3: 888 */
+int main(int argc, char *argv[])
+{
+ struct stat in_stat;
+ struct buffer src_buffer;
+ struct buffer dst_buffer;
+ int ret = 0;
+ unsigned int num_src_buffers = NUM_SRC_BUFS;
+ unsigned int num_dst_buffers = NUM_DST_BUFS;
+
+ parse_args(argc, argv);
+
+ src_fd = open(in_file, O_RDONLY);
+ perror_exit(src_fd < 0, in_file);
+ fstat(src_fd, &in_stat);
+ in_size = in_stat.st_size;
+ src_addr = mmap(0, in_size, PROT_READ, MAP_SHARED, src_fd, 0);
+ perror_exit(src_addr == MAP_FAILED, "mmap");
+
+ init_fb();
+
+ dst_fd = open(out_file, O_RDWR | O_TRUNC | O_CREAT,
+ S_IRUSR | S_IWUSR );
+ perror_exit(dst_fd < 0, out_file);
+ ftruncate(dst_fd, in_size);
+ dst_addr = mmap(0, in_size, PROT_WRITE, MAP_SHARED, dst_fd, 0);
+ perror_exit(dst_addr == MAP_FAILED, "mmap");
+
+ init_video_dev();
+ set_fmt(format);
+
+ if (rotation)
+ set_rotation(rotation);
+ else
+ set_flip(flip);
+
+ request_buffers(&num_src_buffers, &num_dst_buffers);
+ get_buffer(&src_buffer, 0);
+ src_buffer.index = 0;
+ get_buffer(&dst_buffer, 1);
+ dst_buffer.index = 0;
+
+ memcpy(src_buffer.addr, src_addr, framesize);
+ ret = process(&src_buffer, &dst_buffer);
+ if (ret)
+ return ret;
+
+ memcpy(dst_addr, dst_buffer.addr, framesize);
+
+ return 0;
+}
+