/* | |
* V4L2 video capture example | |
* | |
* This program is provided with the V4L2 API | |
* | |
* Copyright (C) 2023 ASR Microelectronics Co., Ltd. | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <assert.h> | |
#include <getopt.h> | |
#include <sys/ioctl.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <errno.h> | |
#include <linux/videodev2.h> | |
#include <sys/mman.h> | |
#include <sys/time.h> | |
#define TRUE (1) | |
#define FALSE (0) | |
#define IMAGE "/tmp/camera/image" | |
#define DIR "/tmp/camera" | |
#define IMAGEWIDTH 640 | |
#define IMAGEHEIGHT 480 | |
#define FRAME_NUM 4 | |
int fd; | |
struct v4l2_buffer buf; | |
static int frame_count = 4; | |
static char *dev_name = "/dev/video0"; | |
struct buffer | |
{ | |
void * start; | |
unsigned int length; | |
long long int timestamp; | |
} *buffers; | |
static int v4l2_init(void) | |
{ | |
struct v4l2_capability cap; | |
struct v4l2_fmtdesc fmtdesc; | |
struct v4l2_format fmt; | |
if ((fd = open(dev_name, O_RDWR)) == -1) | |
{ | |
printf("Error opening V4L interface\n"); | |
exit(EXIT_FAILURE); | |
} | |
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) | |
{ | |
printf("Error opening device %s: unable to query device.\n",dev_name); | |
exit(EXIT_FAILURE); | |
} | |
else | |
{ | |
printf("driver:\t\t%s\n",cap.driver); | |
printf("card:\t\t%s\n",cap.card); | |
printf("bus_info:\t%s\n",cap.bus_info); | |
printf("capabilities:\t%x\n",cap.capabilities); | |
if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) | |
{ | |
printf("Device %s: supports capture.\n",dev_name); | |
} | |
if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) | |
{ | |
printf("Device %s: supports streaming.\n",dev_name); | |
} | |
} | |
fmtdesc.index=0; | |
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
printf("Support format:\r\n"); | |
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1) | |
{ | |
printf("\t%d.%s, 0x%x\r\n",fmtdesc.index+1,fmtdesc.description, fmtdesc.pixelformat); | |
fmtdesc.index++; | |
} | |
struct v4l2_format fmt_test; | |
fmt_test.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
fmt_test.fmt.pix.pixelformat=V4L2_PIX_FMT_YVYU; | |
if(ioctl(fd,VIDIOC_TRY_FMT,&fmt_test)==-1) | |
{ | |
printf("not support format YVYU!\n"); | |
} | |
else | |
{ | |
printf("support format YVYU, 0x%x\r\n", V4L2_PIX_FMT_YVYU); | |
} | |
printf("set fmt...\r\n"); | |
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YVYU; | |
fmt.fmt.pix.height = IMAGEHEIGHT; | |
fmt.fmt.pix.width = IMAGEWIDTH; | |
fmt.fmt.pix.field = V4L2_FIELD_NONE; | |
printf("fmt.type:\t\t%d\n", fmt.type); | |
printf("pix.pixelformat:\t%c%c%c%c\n", fmt.fmt.pix.pixelformat & 0xFF, | |
(fmt.fmt.pix.pixelformat >> 8) & 0xFF, (fmt.fmt.pix.pixelformat >> 16) & 0xFF, | |
(fmt.fmt.pix.pixelformat >> 24) & 0xFF); | |
printf("pix.height:\t\t%d\n", fmt.fmt.pix.height); | |
printf("pix.width:\t\t%d\n", fmt.fmt.pix.width); | |
printf("pix.field:\t\t%d\n", fmt.fmt.pix.field); | |
if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) | |
{ | |
printf("Unable to set format\n"); | |
exit(EXIT_FAILURE); | |
} | |
printf("get fmt...\r\n"); | |
if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -1) | |
{ | |
printf("Unable to get format\n"); | |
exit(EXIT_FAILURE); | |
} | |
printf("fmt.type:\t\t%d\n",fmt.type); | |
printf("pix.pixelformat:\t%c%c%c%c\n", fmt.fmt.pix.pixelformat & 0xFF, | |
(fmt.fmt.pix.pixelformat >> 8) & 0xFF, (fmt.fmt.pix.pixelformat >> 16) & 0xFF, | |
(fmt.fmt.pix.pixelformat >> 24) & 0xFF); | |
printf("pix.height:\t\t%d\n", fmt.fmt.pix.height); | |
printf("pix.width:\t\t%d\n", fmt.fmt.pix.width); | |
printf("pix.field:\t\t%d\n", fmt.fmt.pix.field); | |
return TRUE; | |
} | |
static int v4l2_mem_ops(void) | |
{ | |
unsigned int n_buffers; | |
struct v4l2_requestbuffers req; | |
req.count = FRAME_NUM; | |
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
req.memory = V4L2_MEMORY_MMAP; | |
if(ioctl(fd, VIDIOC_REQBUFS, &req)==-1) | |
{ | |
printf("request for buffers error\n"); | |
return FALSE; | |
} | |
buffers = malloc(req.count * sizeof(*buffers)); | |
if (!buffers) | |
{ | |
printf ("out of memory!\n"); | |
exit(EXIT_FAILURE); | |
} | |
for (n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++) | |
{ | |
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
buf.memory = V4L2_MEMORY_MMAP; | |
buf.index = n_buffers; | |
if (ioctl (fd, VIDIOC_QUERYBUF, &buf) == -1) | |
{ | |
printf("query buffer error\n"); | |
exit(EXIT_FAILURE); | |
} | |
buffers[n_buffers].length = buf.length; | |
buffers[n_buffers].start = mmap(NULL, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, buf.m.offset); | |
if (buffers[n_buffers].start == MAP_FAILED) | |
{ | |
printf("buffer map error\n"); | |
exit(EXIT_FAILURE); | |
} | |
} | |
return TRUE; | |
} | |
static int v4l2_frame_process(void) | |
{ | |
unsigned int n_buffers; | |
enum v4l2_buf_type type; | |
char file_name[100]; | |
char index_str[10]; | |
long long int extra_time = 0; | |
long long int cur_time = 0; | |
long long int last_time = 0; | |
if(access(DIR, 0) !=0 ) | |
{ | |
if(mkdir(DIR, 0755)==-1) | |
{ | |
printf("mkdir %s error\n", DIR); | |
return FALSE; | |
} | |
} | |
for (n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++) | |
{ | |
buf.index = n_buffers; | |
ioctl(fd, VIDIOC_QBUF, &buf); | |
} | |
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
ioctl(fd, VIDIOC_STREAMON, &type); | |
int loop = 0; | |
while(loop < frame_count) | |
{ | |
n_buffers = loop % FRAME_NUM; | |
fd_set fds; | |
struct timeval tv; | |
int r; | |
FD_ZERO(&fds); | |
FD_SET(fd, &fds); | |
tv.tv_sec = 2; | |
tv.tv_usec = 0; | |
r = select(fd + 1, &fds, NULL, NULL, &tv); | |
if (-1 == r) { | |
if (EINTR == errno) | |
continue; | |
printf("select failed, err: %d\n", errno); | |
return FALSE; | |
} | |
if (0 == r) { | |
printf("select failed, err: %d\n", errno); | |
return FALSE; | |
} | |
buf.index = n_buffers; | |
ioctl(fd, VIDIOC_DQBUF, &buf); | |
buffers[n_buffers].timestamp = buf.timestamp.tv_sec*1000000 + buf.timestamp.tv_usec; | |
cur_time = buffers[n_buffers].timestamp; | |
extra_time = cur_time - last_time; | |
last_time = cur_time; | |
printf("time_deta:%lld\n\n", extra_time); | |
memset(file_name, 0, sizeof(file_name)); | |
memset(index_str, 0, sizeof(index_str)); | |
sprintf(index_str, "%d", loop%2); | |
strcpy(file_name, IMAGE); | |
strcat(file_name, index_str); | |
strcat(file_name, ".jpg"); | |
FILE *fp2 = fopen(file_name, "wb"); | |
if(!fp2) | |
{ | |
printf("open %s error\r\n",file_name); | |
return(FALSE); | |
} | |
fwrite(buffers[n_buffers].start, buffers[n_buffers].length, 1, fp2); | |
fclose(fp2); | |
printf("save %s OK\r\n", file_name); | |
ioctl(fd, VIDIOC_QBUF, &buf); | |
loop++; | |
} | |
return TRUE; | |
} | |
static int v4l2_release(void) | |
{ | |
unsigned int n_buffers; | |
enum v4l2_buf_type type; | |
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
ioctl(fd, VIDIOC_STREAMOFF, &type); | |
for(n_buffers=0;n_buffers<FRAME_NUM;n_buffers++) | |
{ | |
munmap(buffers[n_buffers].start,buffers[n_buffers].length); | |
} | |
free(buffers); | |
close(fd); | |
return TRUE; | |
} | |
static void usage(FILE *fp, char **argv) | |
{ | |
fprintf(fp, | |
"Usage: %s [options]\n\n" | |
"Options:\n" | |
"-d | --device name Video device name [%s]\n" | |
"-h | --help Print this message\n" | |
"-c | --count Number of frames to grab [%i]\n" | |
"", | |
argv[0], dev_name, frame_count); | |
} | |
static const char short_options[] = "d:hc:"; | |
static const struct option | |
long_options[] = { | |
{ "device", required_argument, NULL, 'd' }, | |
{ "help", no_argument, NULL, 'h' }, | |
{ "count", required_argument, NULL, 'c' }, | |
{ 0, 0, 0, 0 } | |
}; | |
int main(int argc, char **argv) | |
{ | |
dev_name = "/dev/video0"; | |
for (;;) { | |
int idx; | |
int c; | |
c = getopt_long(argc, argv, | |
short_options, long_options, &idx); | |
if (-1 == c) | |
break; | |
switch (c) { | |
case 0: | |
break; | |
case 'd': | |
dev_name = optarg; | |
break; | |
case 'h': | |
usage(stdout, argv); | |
exit(EXIT_SUCCESS); | |
case 'c': | |
frame_count = strtol(optarg, NULL, 0); | |
if (errno) | |
exit(EXIT_FAILURE); | |
break; | |
default: | |
usage(stderr, argv); | |
exit(EXIT_FAILURE); | |
} | |
} | |
v4l2_init(); | |
v4l2_mem_ops(); | |
v4l2_frame_process(); | |
v4l2_release(); | |
return TRUE; | |
} | |