【拍照程序:test\ov5640\ov5640.c】
#include <fcntl.h>
#include <linux/v4l2-subdev.h>
#include <linux/videodev2.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "bmp.h"
int yuv2rgb(int y, int u, int v, uint8_t *_r, uint8_t *_g, uint8_t *_b)
{
int r, g, b;
// https://blog.csdn.net/u012294613/article/details/141095964
r = y + 1403 * (v - 128) / 1000;
g = y - 343 * (u - 128) / 1000 - 714 * (v - 128) / 1000;
b = y + 1770 * (u - 128) / 1000;
if (r > 255)
r = 255;
else if (r < 0)
r = 0;
if (g > 255)
g = 255;
else if (g < 0)
g = 0;
if (b > 255)
b = 255;
else if (b < 0)
b = 0;
*_r = (uint8_t)r;
*_g = (uint8_t)g;
*_b = (uint8_t)b;
return (r << 16) | (g << 8) | b;
}
void convert_to_bitmap(const uint8_t *data, long size, const char *filename, int width, int height)
{
int bmpsize, stride;
int i, j, m, n;
int y1, u, y2, v;
uint8_t *bmpdata;
BITMAPFILEHEADER bmpfilehdr = {0};
BITMAPINFOHEADER bmphdr = {0};
FILE *fp;
fp = fopen(filename, "wb");
if (fp == NULL)
return;
memcpy(&bmpfilehdr.bfType, "BM", 2);
bmpfilehdr.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bmphdr.biSize = sizeof(BITMAPINFOHEADER);
bmphdr.biWidth = width;
bmphdr.biHeight = -height;
bmphdr.biPlanes = 1;
bmphdr.biBitCount = 24;
bmphdr.biCompression = BI_RGB;
stride = ((((width * bmphdr.biBitCount) + 31) & ~31) >> 3);
bmpsize = height * stride;
bmpfilehdr.bfSize = bmpfilehdr.bfOffBits + bmpsize;
fwrite(&bmpfilehdr, sizeof(BITMAPFILEHEADER), 1, fp);
fwrite(&bmphdr, bmphdr.biSize, 1, fp);
bmpdata = malloc(bmpsize);
if (bmpdata != NULL)
{
m = 0;
n = 0;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j += 2)
{
if (m + 4 > size)
break;
y1 = data[m];
u = data[m + 1];
y2 = data[m + 2];
v = data[m + 3];
yuv2rgb(y1, u, v, &bmpdata[n + j * 3 + 2], &bmpdata[n + j * 3 + 1], &bmpdata[n + j * 3]);
yuv2rgb(y2, u, v, &bmpdata[n + j * 3 + 5], &bmpdata[n + j * 3 + 4], &bmpdata[n + j * 3 + 3]);
m += 4;
}
n += stride;
}
fwrite(bmpdata, 1, bmpsize, fp);
free(bmpdata);
}
fclose(fp);
}
int main()
{
char filename[50];
int fd;
int i, ret;
struct v4l2_subdev_frame_interval subdev_interval;
struct v4l2_subdev_format subdev_format;
struct v4l2_capability cap;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_format format;
struct v4l2_requestbuffers reqbufs;
struct v4l2_buffer buf;
struct v4l2_plane plane;
unsigned char *mem[4];
unsigned int memsize[4];
system("rm *.bin *.bmp photos/*.bmp 2>/dev/null");
fd = open("/dev/v4l-subdev0", O_RDWR);
if (fd == -1)
{
printf("failed to open /dev/v4l-subdev0\n");
return -1;
}
subdev_interval.interval.numerator = 1;
subdev_interval.interval.denominator = 15;
subdev_interval.pad = 0;
ret = ioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &subdev_interval);
if (ret == -1)
printf("VIDIOC_SUBDEV_S_FRAME_INTERVAL failed\n");
else
printf("interval=%d/%d\n", subdev_interval.interval.numerator, subdev_interval.interval.denominator);
subdev_format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
subdev_format.pad = 0;
ret = ioctl(fd, VIDIOC_SUBDEV_G_FMT, &subdev_format);
if (ret == -1)
printf("VIDIOC_SUBDEV_G_FMT failed\n");
else
printf("code=0x%x, field=%d\n", subdev_format.format.code, subdev_format.format.field);
subdev_format.format.width = 2592;
subdev_format.format.height = 1944;
ret = ioctl(fd, VIDIOC_SUBDEV_S_FMT, &subdev_format);
if (ret == -1)
printf("VIDIOC_SUBDEV_S_FMT failed\n");
close(fd);
fd = open("/dev/video0", O_RDWR);
if (fd == -1)
{
printf("failed to open /dev/video0\n");
return -1;
}
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
if (ret == -1)
printf("VIDIOC_QUERYCAP failed\n");
else
{
printf("Driver: %s\n", cap.driver);
printf("Card: %s\n", cap.card);
printf("Bus info: %s\n", cap.bus_info);
printf("Version: %d\n", cap.version);
}
printf("VIDIOC_ENUM_FMT start\n");
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
{
printf("\t%d.%s\n", fmtdesc.index + 1, fmtdesc.description);
fmtdesc.index++;
}
printf("VIDIOC_ENUM_FMT end\n");
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
format.fmt.pix_mp.width = subdev_format.format.width;
format.fmt.pix_mp.height = subdev_format.format.height;
format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUYV;
printf("Size: %dx%d\n", format.fmt.pix_mp.width, format.fmt.pix_mp.height);
ret = ioctl(fd, VIDIOC_S_FMT, &format);
if (ret == -1)
printf("VIDIOC_S_FMT failed\n");
printf("Size: %dx%d\n", format.fmt.pix_mp.width, format.fmt.pix_mp.height);
reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
reqbufs.count = sizeof(mem) / sizeof(mem[0]);
reqbufs.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_REQBUFS, &reqbufs);
if (ret == -1)
printf("VIDIOC_REQBUFS failed\n");
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
buf.memory = V4L2_MEMORY_MMAP;
buf.m.planes = &plane;
buf.length = 1;
for (i = 0; i < reqbufs.count; i++)
{
// allocate buffers to have somewhere to store the images
buf.index = i;
ret = ioctl(fd, VIDIOC_QUERYBUF, &buf);
if (ret == -1)
{
printf("VIDIOC_QUERYBUF failed: i=%d\n", i);
break;
}
// query the physical address of each allocated buffer in order to mmap() those
printf("i=%d, buf.length=%d, buf.m.planes[0]=%d, buf.m.planes[0].m.mem_offset=%d\n", i, buf.length, buf.m.planes[0].length, buf.m.planes[0].m.mem_offset);
mem[i] = mmap(NULL, buf.m.planes[0].length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.planes[0].m.mem_offset);
memsize[i] = buf.m.planes[0].length;
if (mem[i] == MAP_FAILED)
printf("map failed: i=%d\n", i);
memset(mem[i], 0, memsize[i]);
// Before the buffers can be filled with data, the buffers has to be enqueued.
// Enqueued buffers will lock the memory pages used so that those cannot be swapped out during usage.
// The buffers remain locked until that are dequeued.
ret = ioctl(fd, VIDIOC_QBUF, &buf);
if (ret == -1)
printf("VIDIOC_QBUF failed: i=%d\n", i);
}
// start acquire video frames and use the queued buffers to store them
ret = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
ret = ioctl(fd, VIDIOC_STREAMON, &ret);
if (ret == -1)
printf("VIDIOC_STREAMON failed\n");
for (i = 0; i < 50; i++)
{
// Once buffers are filled with video data, those are ready to be dequeued and consumed by the application.
// This ioctl will be blocking (unless O_NONBLOCK is used) until a buffer is available.
// VIDIOC_DQBUF works similar to VIDIOC_QBUF but it populates the v4l2_buffer.index field with the index number
// of the buffer that has been dequeued.
buf.index = i % reqbufs.count;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
ret = ioctl(fd, VIDIOC_DQBUF, &buf);
if (ret == -1)
printf("VIDIOC_DQBUF failed: i=%d, buf.index=%d\n", i, buf.index);
else
{
printf("i=%d, buf.index=%d, buf.length=%d, buf.m.planes[0].length=%d\n", i, buf.index, buf.length, buf.m.planes[0].length);
if (access("photos", 0) == -1)
mkdir("photos", 0755);
snprintf(filename, sizeof(filename), "photos/photo%d.bmp", i);
convert_to_bitmap(mem[buf.index], buf.m.planes[0].length, filename, format.fmt.pix_mp.width, format.fmt.pix_mp.height);
}
// As soon the buffer is dequeued and processed, the application has to immediately queue back the buffer
// so that the driver layer can fill it with new frames.
// This is usually part of the application main-loop.
ret = ioctl(fd, VIDIOC_QBUF, &buf);
if (ret == -1)
printf("VIDIOC_QBUF failed: buf.index=%d\n", buf.index);
}
// Once we are done with the video capturing, we can stop the streaming.
// This will unlock all enqueued buffers and stop capture frames.
ret = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
ret = ioctl(fd, VIDIOC_STREAMOFF, &ret);
if (ret == -1)
printf("VIDIOC_STREAMOFF failed\n");
for (i = 0; i < reqbufs.count; i++)
{
if (mem[i] != NULL && mem[i] != MAP_FAILED)
munmap(mem[i], memsize[i]);
}
close(fd);
return ret;
}