#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>
#include <sys/mman.h>
#include <jpeglib.h>
#include <linux/fb.h>
int read_JPEG_file(const char *jpegData, char *rgbdata, int size)
{
struct jpeg_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
cinfo.err = jpeg_std_error(&jerr);
//1.
jpeg_create_decompress(&cinfo);
//2.
jpeg_mem_src(&cinfo, (unsigned char *)jpegData, size);
//3.
(void) jpeg_read_header(&cinfo, TRUE);
//4.
(void) jpeg_start_decompress(&cinfo);
//5.
int row_stride = cinfo.output_width * cinfo.output_components;
unsigned char *buffer = malloc(row_stride);
int i=0;
while (cinfo.output_scanline < cinfo.output_height)
{
(void) jpeg_read_scanlines(&cinfo, &buffer, 1);
memcpy(rgbdata + i * 640 * 3, buffer, row_stride);
i ++;
}
//6.
(void) jpeg_finish_decompress(&cinfo);
//7.
jpeg_destroy_decompress(&cinfo);
return 1;
}
int lcdfd = 0;
unsigned int *lcdptr = NULL;
int lcd_w = 800, lcd_h = 480;
void lcd_show_rgb(unsigned char *rgbdata, int w, int h)
{
unsigned int *ptr = lcdptr;
for (int i=0; i<h; i++)
{
for (int j=0; j<w; j++)
{
memcpy(ptr + j, rgbdata + j*3, 3);
}
ptr += lcd_w;
rgbdata += w*3;
}
}
int main()
{
lcdfd = open("/dev/fb0", O_RDWR);
if (lcdfd < 0)
{
perror("Open /dev/fb0 failed");
return -1;
}
struct fb_var_screeninfo info;
int lret = ioctl(lcdfd, FBIOGET_VSCREENINFO, &info);
lcd_w = info.xres_virtual;
lcd_h = info.yres_virtual;
lcdptr = (unsigned int *)mmap(NULL, lcd_w*lcd_h*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcdfd, 0);
char file_name[16] = {0};
int fd, ret, video_id = 0, i = 0;
// 1. Open device
sprintf(file_name, "%s%d", "/dev/video", video_id);
fd = open(file_name, O_RDWR);
if (fd < 0)
{
perror("Open device failed");
return -1;
}
// 2. Get device support format
//#define VIDIOC_ENUM_FMT _IOWR('V', 2, struct v4l2_fmtdesc)
struct v4l2_fmtdesc vfmtdesc;
do
{
vfmtdesc.index = i;
vfmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_ENUM_FMT, &vfmtdesc);
if (ret < 0)
{
perror("Get format failed");
break;
}
printf("index = %d\n", vfmtdesc.index);
printf("type = %d\n", vfmtdesc.type);
printf("flags = %d\n", vfmtdesc.flags);
printf("description = %s\n", vfmtdesc.description);
printf("pixelformat = %c%c%c%c\n", ((vfmtdesc.pixelformat >> 0) & 0xFF), ((vfmtdesc.pixelformat >> 8) & 0xFF), ((vfmtdesc.pixelformat >> 16) & 0xFF), ((vfmtdesc.pixelformat >> 24) & 0xFF));
printf("reserved[0] = %d\n", vfmtdesc.reserved[0]);
i ++;
} while (1);
// 3. Set device capture format
//#define VIDIOC_G_FMT _IOWR('V', 4, struct v4l2_format)
//#define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format)
struct v4l2_format vfmt;
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vfmt.fmt.pix.width = 640;
vfmt.fmt.pix.height = 480;
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
if (ret < 0)
{
perror("\tSet device capture format failed");
return -1;
}
memset(&vfmt, 0, sizeof(vfmt));
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);
if (ret < 0)
{
perror("Get device capture format failed");
return -1;
}
if ( (vfmt.fmt.pix.width == 640) && (vfmt.fmt.pix.height == 480)
&& (vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) )
{
printf("Set Ok\n");
}
else
{
printf("Set Failed\n");
}
// 4. request kernel cache buffer queue
//#define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers)
struct v4l2_requestbuffers vrequestbuffers;
vrequestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vrequestbuffers.count = 4;
vrequestbuffers.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_REQBUFS, &vrequestbuffers);
if (ret < 0)
{
perror("Request kernel cache buffer queue failed");
return -1;
}
// 5. mmap
//#define VIDIOC_QUERYBUF _IOWR('V', 9, struct v4l2_buffer)
//#define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer)
unsigned char *mptr[4];
unsigned int size[4];
struct v4l2_buffer vbuffer;
vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for (i=0; i<4; i++)
{
vbuffer.index = i;
ret = ioctl(fd, VIDIOC_QUERYBUF, &vbuffer);
if (ret < 0)
{
perror("Query kernel cache buffer queue failed");
return -1;
}
mptr[i] = mmap(NULL, vbuffer.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, vbuffer.m.offset);
size[i] = vbuffer.length;
ret = ioctl(fd, VIDIOC_QBUF, &vbuffer);
if (ret < 0)
{
perror("Putdown kernel cache buffer queue failed");
return -1;
}
}
// 6. Start capture
//#define VIDIOC_STREAMON _IOW('V', 18, int)
//#define VIDIOC_DQBUF _IOWR('V', 17, struct v4l2_buffer)
//#define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer)
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_STREAMON, &type);
if (ret < 0)
{
perror("Start capture failed");
return -1;
}
// Display LCD
unsigned char rgbdata[640*480*3];
while (1)
{
struct v4l2_buffer readbuf;
readbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_DQBUF, &readbuf);
if (ret < 0)
{
perror("Get buf failed");
return -1;
}
read_JPEG_file(mptr[readbuf.index], rgbdata, readbuf.length);
lcd_show_rgb(rgbdata, 640, 480);
ret = ioctl(fd, VIDIOC_QBUF, &readbuf);
if (ret < 0)
{
perror("Release buf failed");
return -1;
}
}
// 7. Stop capture
//#define VIDIOC_STREAMOFF _IOW('V', 19, int)
ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
if (ret < 0)
{
perror("Stop capture failed");
return -1;
}
// 8. Release mmap
for (i=0; i<4; i++)
{
munmap(mptr[i], size[i]);
}
// 9. Close device
close(fd);
return 0;
}
编译命令:
$ gcc video_v4l2.c -ljpeg
Note: 该程序不能使用界面运行,需要远程运行,操作步骤如下:
1. 按 Ctrl + Alt + F1
进入登录界面。
2. 在命令行使用 sudo 运行程序,则可以看到摄像头采集到的数据显示在屏幕上。
3. 按 Ctrl + Alt + F7
返回图形界面。