camera応用

13969 ワード

この間、cameraをテストするアプリケーションを書きました.簡単にプラットフォームで写真を撮ったり録画したりする機能を実現しました.bmpファイルヘッダは固定解像度640 x 480しかサポートしていません.
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h> 
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <linux/videodev2.h>

#define CLIP_MIN (-278)
#define CLIP_MAX (535)

static volatile int clip_init_done = 0;
static uint8_t clip[CLIP_MAX - CLIP_MIN + 1];

uint8_t *init_clip()
{
	int i;
	
	if(clip_init_done)
		return &clip[-CLIP_MIN];

	for (i = CLIP_MIN; i <= CLIP_MAX; ++i)
		clip[i - CLIP_MIN] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;

	clip_init_done = 1;

	return &clip[-CLIP_MIN];
}

/*
 * Convert YUV planner to RGB565
 *
 * For YUV420: y_stride = width, yu_stride = width / 2
 * For YUV422: y_stride = width, yu_stride = width
 * src_y src_u src_v: start address of Y U V data block in source frame.
 * dst: start address of results frame.
 */
static void yuv_planar_2_rgb(size_t width, size_t height,
			size_t y_stride, size_t uv_stride,
			const uint8_t *src_y, const uint8_t *src_u,
			const uint8_t *src_v, void *dst)
{
	size_t x, y;
	uint8_t *adj_clip = init_clip();
	uint32_t *dst_ptr = (uint32_t *)dst;
	int y1, y2, u, v, u_b, u_g, v_g, v_r, tmp1, b1, g1, r1, tmp2, b2, g2, r2;
	uint32_t rgb1, rgb2;

	for (y = 0; y < height; ++y) {
		for (x = 0; x < width; x += 2) {
			y1 = (int)src_y[x] - 16;
			y2 = (int)src_y[x + 1] - 16;

			u = (int)src_u[x / 2] - 128;
			v = (int)src_v[x / 2] - 128;

			u_b = u * 517;
			u_g = -u * 100;
			v_g = -v * 208;
			v_r = v * 409;

			tmp1 = y1 * 298;
			b1 = (tmp1 + u_b) / 256;
			g1 = (tmp1 + v_g + u_g) / 256;
			r1 = (tmp1 + v_r) / 256;

			tmp2 = y2 * 298;
			b2 = (tmp2 + u_b) / 256;
			g2 = (tmp2 + v_g + u_g) / 256;
			r2 = (tmp2 + v_r) / 256;

			rgb1 = ((adj_clip[r1] >> 3) << 11)
				| ((adj_clip[g1] >> 2) << 5)
				| (adj_clip[b1] >> 3);

			rgb2 = ((adj_clip[r2] >> 3) << 11)
				| ((adj_clip[g2] >> 2) << 5)
				| (adj_clip[b2] >> 3);

			dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
		}

		src_y += y_stride;

		if (y & 1) {
			src_u += uv_stride;
			src_v += uv_stride;
		}

		dst_ptr += width / 2;
	}
}

/*
 * Convert RGB565 to RGB888
 *
 * src: start address of RGB565 data block in source frame.
 * dst: start address of results frame.
 */
static void rgb565_2_rgb888(uint16_t *src, uint8_t *dst, int width, int height)
{
	int i;

	for(i = 0; i < width * height; i++, src++, dst += 3)
	{
		/*  : b、g、r,   8 bit   */
		dst[0] = (uint8_t)(((*src & 0x001f) << 3) | ((*src & 0x001f) >> 2));
		dst[1] = (uint8_t)((((*src & 0x07e0) >> 5) << 2) | (((*src & 0x07e0) >> 5) >> 4));
		dst[2] = (uint8_t)(((*src >> 11) << 3) | ((*src >> 11) >> 2));
	}
}

/* used for calculate FPS */
static struct timeval start, end;
static unsigned long long g_tick, g_precision = 1000000;

static inline void clock_start()
{
	gettimeofday(&start, NULL);
	return;
}

/* for calculate fps */
static inline void clock_end()
{
	struct timeval val;
	gettimeofday(&end, NULL);
	timersub(&end, &start, &val);
	g_tick = val.tv_sec * 1000000 + val.tv_usec;
	return;
}

/* bmp file's head */
char bmp_head[] = {0x42, 0x4d, 0x36, 0x10, 0x0e, 0x00, 0x00, 0x00,
				   0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
				   0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0xe0, 0x01,
				   0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
				   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x0e,
				   0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00,
				   0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

struct cam_buffer {
	void *start;
	void *vstart;
	unsigned long length;
};

static struct cam_info {
	int num;                            /* pic statistics */
	int count;                          /* take pic number, -1 means not stop */
	int dev_fd;                         /* camera dev file descriptor */
	int yuv_fd;                         /* camera yuv file descriptor */
	int rgb_fd;                         /* camera rgb file descriptor */

	unsigned long index;                /* buffer index */
	unsigned long nbuffer;              /* total buffer number */
	unsigned long bufsize;              /* buffer size */
	unsigned long stop_stream;          /* flag indicate to stop work */

	struct cam_buffer *buffers;         /* mmaped buffer info */	
	struct v4l2_format format;          /* pic format */
	struct v4l2_requestbuffers req;
} cam_info;

static void open_yuv_file()
{
	cam_info.yuv_fd = open("/sdcard/cam_test.yuv", O_CREAT | O_TRUNC | O_RDWR, 0777);
	if (cam_info.yuv_fd < 0) {
		printf("open /sdcard/cam_test.yuv failed!
"); } } static void open_rgb_file() { cam_info.rgb_fd = open("/sdcard/cam_test.bmp", O_CREAT | O_TRUNC | O_RDWR, 0777); if (cam_info.rgb_fd < 0) { printf("open /sdcard/cam_test.bmp failed!
"); } } static int start_capturing() { int ret; unsigned long i; struct v4l2_buffer buf; enum v4l2_buf_type type; for (i = 0; i < cam_info.nbuffer; ++i) { memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; ret = ioctl(cam_info.dev_fd, VIDIOC_QBUF, &buf); if (ret < 0) { printf("ioctl VIDIOC_QBUF failed, %d
", errno); return -1; } } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(cam_info.dev_fd, VIDIOC_STREAMON, &type); if (ret < 0) { printf("ioctl VIDIOC_STREAMON failed!, %d
", errno); return -1; } return 0; } static int write_frame() { int ret; struct v4l2_buffer buf; memset(&buf, 0, sizeof(struct v4l2_buffer)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ret = ioctl(cam_info.dev_fd, VIDIOC_DQBUF, &buf); if (ret < 0) { if(errno == EPIPE) { printf("Streaming stop, we need restart stream
"); start_capturing(); return 0; } else { printf("ioctl VIDIOC_DQBUF failed!, %d
", errno); return -1; } } if (cam_info.nbuffer < buf.index) { printf("Kernel return wrong index %d, totoal %lu
", buf.index, cam_info.nbuffer); return -1; } ret = write(cam_info.yuv_fd, cam_info.buffers[buf.index].vstart, cam_info.format.fmt.pix.sizeimage); if(ret < cam_info.format.fmt.pix.sizeimage) { printf("write failed, ret = %d
", ret); return ret; } ret = ioctl(cam_info.dev_fd, VIDIOC_QBUF, &buf); if (ret < 0) { printf("ioctl VIDIOC_QBUF failed!, %d
", errno); return -1; } cam_info.num++; return 0; } static int stop_capturing() { int ret; enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(cam_info.dev_fd, VIDIOC_STREAMOFF, &type); if (ret < 0) { printf("ioctl VIDIOC_STREAMOFF failed!, %d
", errno); return -1; } return 0; } static int start_preview() { int ret; fd_set fds; struct timeval tv; /* Timeout. */ tv.tv_sec = 5; tv.tv_usec = 0; open_yuv_file(); clock_start(); while (!cam_info.stop_stream && (cam_info.count < 0 || cam_info.num < cam_info.count)) { FD_ZERO(&fds); FD_SET(cam_info.dev_fd, &fds); ret = select(cam_info.dev_fd + 1, &fds, NULL, NULL, &tv); if (ret < 0) { printf("select() failed, %d
", errno); break; } if (ret == 0) printf("select() timeout
"); ret = write_frame(); if (ret < 0) break; } clock_end(); printf("
get %d frame, fps: %f
", cam_info.num, (cam_info.num * 1.0 * g_precision) / g_tick); cam_info.num = 0; close(cam_info.yuv_fd); stop_capturing(); printf("preview done, streaming off
"); return ret; } static int init_device() { int i, ret; struct v4l2_buffer buf; struct v4l2_capability cap; /* capabilities related */ memset(&cap, 0, sizeof(cap)); ret = ioctl(cam_info.dev_fd, VIDIOC_QUERYCAP, &cap); if (ret < 0) { printf("ioctl VIDIOC_QUERYCAP failed! ret = %d
", ret); return -1; } if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { printf("Opening a no video capture device
"); return -1; } if (!(cap.capabilities & V4L2_CAP_STREAMING)) { printf("device does not support streaming i/o
"); return -1; } /* set format */ cam_info.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cam_info.format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; ret = ioctl(cam_info.dev_fd, VIDIOC_S_FMT, &cam_info.format); if (ret < 0) { printf("ioctl VIDIOC_S_FMT failed! ret = %d
", ret); return -1; } /* request buffer */ cam_info.req.count = cam_info.nbuffer; cam_info.req.memory = V4L2_MEMORY_MMAP; cam_info.req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(cam_info.dev_fd, VIDIOC_REQBUFS, &cam_info.req); if (ret < 0) { printf("ioctl VIDIOC_REQBUFS failed! ret = %d
", ret); return -1; } cam_info.buffers = malloc(cam_info.req.count * sizeof(struct cam_buffer)); if (!cam_info.buffers) { printf("malloc() failed!
"); return -1; } /* query buffer */ for (i = 0; i < cam_info.req.count; i++) { memset(&buf, 0, sizeof(buf)); buf.index = i; buf.memory = V4L2_MEMORY_MMAP; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(cam_info.dev_fd, VIDIOC_QUERYBUF, &buf); if (ret < 0) { printf("ioctl VIDIOC_QUERYBUF failed! ret = %d
", ret); free(cam_info.buffers); return -1; } cam_info.buffers[i].length = buf.length; cam_info.buffers[i].vstart = mmap (NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, cam_info.dev_fd, buf.m.offset); printf("mmap virt %p, phy %p, size %lu
", (void *)cam_info.buffers[i].vstart, (void *)buf.reserved, (unsigned long)buf.length); if (MAP_FAILED == cam_info.buffers[i].vstart) { printf("mmap() failed!
"); free(cam_info.buffers); return -1; } } return 0; } /* read frame */ static int take_photo() { int i, ret, len, rgb565_len, rgb888_len; char *buffer, *rgb565_buffer, *rgb888_buffer; len = cam_info.format.fmt.pix.sizeimage; rgb565_len = cam_info.format.fmt.pix.sizeimage; rgb888_len = cam_info.format.fmt.pix.sizeimage * 3 / 2; buffer = malloc(len); rgb565_buffer = malloc(rgb565_len); rgb888_buffer = malloc(rgb888_len); open_yuv_file(); clock_start(); for(i = 0; i < cam_info.req.count; i++) { ret = read(cam_info.dev_fd, buffer, len); if(ret < len) { printf("read failed, ret = %d, len = %d
", ret, len); goto exit; } ret = write(cam_info.yuv_fd, buffer, len); if(ret < len) { printf("write failed, ret = %d
", ret); goto exit; } } clock_end(); close(cam_info.yuv_fd); printf("get %d frame, fps: %f
", i, (i * 1.0 * g_precision) / g_tick); open_rgb_file(); ret = write(cam_info.rgb_fd, bmp_head, sizeof(bmp_head)); if(ret < sizeof(bmp_head)) { printf("write bmp_head failed, ret = %d
", ret); } yuv_planar_2_rgb(cam_info.format.fmt.pix.width, cam_info.format.fmt.pix.height, cam_info.format.fmt.pix.width, cam_info.format.fmt.pix.width, (const uint8_t *)buffer, (const uint8_t *)(buffer + rgb565_len / 2), (const uint8_t *)(buffer + 3 * rgb565_len / 4), rgb565_buffer); rgb565_2_rgb888(rgb565_buffer, rgb888_buffer, cam_info.format.fmt.pix.width, cam_info.format.fmt.pix.height); ret = write(cam_info.rgb_fd, rgb888_buffer, rgb888_len); if(ret < rgb888_len) { printf("write rgb_buffer failed, ret = %d
", ret); goto exit; } close(cam_info.rgb_fd); free(buffer); free(rgb565_buffer); free(rgb888_buffer); return 0; exit: free(buffer); free(rgb565_buffer); free(rgb888_buffer); return ret; } static int take_video() { int ret; ret = start_capturing(); if(ret < 0) return ret; return start_preview(); } static void show_usage() { printf("cam_test -d <dev> -c <mode> -w <width> -h <height> -n <count>
"); printf("Options:
"); printf(" -d: device num. 0 - back, 1 - front.
"); printf(" -c: capture mode. 0 - video, 1 - photo.
"); printf(" -w: specify frame width.
"); printf(" -h: specify frame height.
"); printf(" -n: specify the count of frames.
"); } int main(int argc, char *argv[]) { int c, cam_dev, cam_ctl, pix_width, pix_height, pix_count; if (argc < 2) { show_usage(); return -1; } while ((c = getopt(argc, argv, "d:c:n:h:w:")) != -1) { switch(c) { case 'd': cam_dev = atoi(optarg); break; case 'c': cam_ctl = atoi(optarg); break; case 'n': pix_count = atoi(optarg); break; case 'h': pix_height = atoi(optarg); break; case 'w': pix_width = atoi(optarg); break; default: show_usage(); return -1; } } printf("pix_width = %d, pix_height = %d, pix_count = %d
", pix_width, pix_height, pix_count); if(cam_dev) cam_info.dev_fd = open("/dev/video1", O_RDWR); else cam_info.dev_fd = open("/dev/video0", O_RDWR); cam_info.format.fmt.pix.width = pix_width; cam_info.format.fmt.pix.height = pix_height; cam_info.count = pix_count; cam_info.nbuffer = 10; cam_info.nbuffer = cam_info.nbuffer < pix_count ? cam_info.nbuffer : pix_count; init_device(); if(cam_ctl) take_photo(); else take_video(); close(cam_info.dev_fd); return 0; }