图11
经过修改调试代码后,最后在板上成功的运行,并输出开发板对摄像头支持的格式列表,如下:
Supported Formats: V4L2_CAP_VIDEO_CAPTURE: pixelformat = YUYV, description = 'YUYV 4:2:2' resolution:640x480 fps: 30, 25, 20, 15, 10, 5 resolution:160x120 fps: 30, 25, 20, 15, 10, 5 resolution:176x144 fps: 30, 25, 20, 15, 10, 5 resolution:320x176 fps: 30, 25, 20, 15, 10, 5 resolution:320x240 fps: 30, 25, 20, 15, 10, 5 resolution:352x288 fps: 30, 25, 20, 15, 10, 5 resolution:432x240 fps: 30, 25, 20, 15, 10, 5 resolution:544x288 fps: 30, 25, 20, 15, 10, 5 resolution:640x360 fps: 30, 25, 20, 15, 10, 5 resolution:752x416 fps: 25, 20, 15, 10, 5 resolution:800x448 fps: 25, 20, 15, 10, 5 resolution:800x600 fps: 20, 15, 10, 5 resolution:864x480 fps: 20, 15, 10, 5 resolution:960x544 fps: 15, 10, 5 resolution:960x720 fps: 10, 5 resolution:1024x576 fps: 10, 5 resolution:1184x656 fps: 10, 5 resolution:1280x720 fps: 10, 5 resolution:1280x960 fps: 7, 5 V4L2_CAP_VIDEO_CAPTURE: pixelformat = MJPG, description = 'Motion-JPEG' resolution:640x480 fps: 30, 25, 20, 15, 10, 5 resolution:160x120 fps: 30, 25, 20, 15, 10, 5 resolution:176x144 fps: 30, 25, 20, 15, 10, 5 resolution:320x176 fps: 30, 25, 20, 15, 10, 5 resolution:320x240 fps: 30, 25, 20, 15, 10, 5 resolution:352x288 fps: 30, 25, 20, 15, 10, 5 resolution:432x240 fps: 30, 25, 20, 15, 10, 5 resolution:544x288 fps: 30, 25, 20, 15, 10, 5 resolution:640x360 fps: 30, 25, 20, 15, 10, 5 resolution:752x416 fps: 30, 25, 20, 15, 10, 5 resolution:800x448 fps: 30, 25, 20, 15, 10, 5 resolution:800x600 fps: 30, 25, 20, 15, 10, 5 resolution:864x480 fps: 30, 25, 20, 15, 10, 5 resolution:960x544 fps: 30, 25, 20, 15, 10, 5 resolution:960x720 fps: 30, 25, 20, 15, 10, 5 resolution:1024x576 fps: 30, 25, 20, 15, 10, 5 resolution:1184x656 fps: 30, 25, 20, 15, 10, 5 resolution:1280x720 fps: 30, 25, 20, 15, 10, 5 resolution:1280x960 fps: 30, 25, 20, 15, 10, 5 五、采集程序开发测试 获得摄像头的设备参数以及能够采集的视频规格参数以后,就可以开发视频采集程序,在开发采集程序前,还需要查看一下摄像头的用户层抽象操作设备,一般在/dev/下,使用以下命令查看抽象出的设备文件:
ls –ls /dev/video*
可以看到抽象的视频采集设备,后面将试用设备控制代码来打开操作它。
另外在看一下采集设备对 v4l2 系统的驱动,命令如下:
ls -l/sys/class/video4linux/video*
这两个命令是在摄像头底层驱动加载工作正常后才会出现,如果底层驱动有任何问题,这两个命令将不会有相应信息显示。
采集视频采用的是V4L2视频框架。V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video*下,如果只有一个视频设备,通常为/dev/video0。V4L2在设计时,是能支持很多广泛的设备,但它们之中只有一部分是真正的视频设备:
可以支持多种设备,它可以有以下几种接口:
1. 视频采集接口(video capture interface):这种应用的设备可以是高频头或者摄像头.V4L2的最初设计就是应用于这种功能的.
2. 视频输出接口(video output interface):可以驱动计算机的外围视频图像设备--像可以输出电视信号格式的设备.
3. 直接传输视频接口(video overlay interface):它的主要工作是把从视频采集设备采集过来的信号直接输出到输出设备之上,而不用经过系统的CPU.
4. 视频间隔消隐信号接口(VBI interface):它可以使应用可以访问传输消隐期的视频信号.
5. 收音机接口(radio interface):可用来处理从AM或FM高频头设备接收来的音频流.
图12
图13
代码比较长,这里放出修改的部分关键代码如下:
-
-
#include
-
#include
-
#include
-
#include
-
-
#include /* getopt_long() */
-
-
#include /* low-level i/o */
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
-
#include
-
-
#define CLEAR(x) memset(&(x), 0, sizeof(x))
-
-
#ifndef V4L2_PIX_FMT_H264
-
#define V4L2_PIX_FMT_H264 v4l2_fourcc('H', '2', '6', '4') /* H264 with start codes */
-
#endif
-
-
enum io_method {
-
IO_METHOD_READ,
-
IO_METHOD_MMAP,
-
IO_METHOD_USERPTR,
-
};
-
-
struct buffer {
-
void *start;
-
size_t length;
-
};
-
-
static char *dev_name;
-
static enum io_method io = IO_METHOD_MMAP;
-
static int fd = -1;
-
struct buffer *buffers;
-
static unsigned int n_buffers;
-
static int out_buf;
-
static int force_format = 0;
-
static int frame_count = 200;
-
static int frame_number = 0;
-
-
static void errno_exit(const char *s)
-
{
-
fprintf(stderr, "%s error %d, %sn", s, errno, strerror(errno));
-
exit(EXIT_FAILURE);
-
}
-
-
static int xioctl(int fh, int request, void *arg)
-
{
-
int r;
-
-
do {
-
r = ioctl(fh, request, arg);
-
} while (-1 == r && EINTR == errno);
-
-
return r;
-
}
-
-
static void process_image(const void *p, int size)
-
{
-
int status;
-
frame_number++;
-
-
if (out_buf==0)
-
{
-
/* write to file */
-
FILE *fp=fopen("video.raw","ab");
-
fwrite(p, size, 1, fp);
-
fflush(fp);
-
fclose(fp);
-
}
-
else
-
{
-
/* write to stdout */
-
status = write(1, p, size);
-
if(status == -1)
-
perror("write");
-
}
-
}
-
-
static int read_frame(void)
-
{
-
struct v4l2_buffer buf;
-
unsigned int i;
-
-
switch (io) {
-
case IO_METHOD_READ:
-
if (-1 == read(fd, buffers[0].start, buffers[0].length)) {
-
switch (errno) {
-
case EAGAIN:
-
return 0;
-
-
case EIO:
-
/* Could ignore EIO, see spec. */
-
-
/* fall through */
-
-
default:
-
errno_exit("read");
-
}
-
}
-
-
process_image(buffers[0].start, buffers[0].length);
-
break;
-
-
case IO_METHOD_MMAP:
-
CLEAR(buf);
-
-
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
buf.memory = V4L2_MEMORY_MMAP;
-
-
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
-
switch (errno) {
-
case EAGAIN:
-
return 0;
-
-
case EIO:
-
/* Could ignore EIO, see spec. */
-
-
/* fall through */
-
-
default:
-
errno_exit("VIDIOC_DQBUF");
-
}
-
}
-
-
assert(buf.index < n_buffers);
-
-
process_image(buffers[buf.index].start, buf.bytesused);
-
-
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
-
errno_exit("VIDIOC_QBUF");
-
break;
-
-
case IO_METHOD_USERPTR:
-
CLEAR(buf);
-
-
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
buf.memory = V4L2_MEMORY_USERPTR;
-
-
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
-
switch (errno) {
-
case EAGAIN:
-
return 0;
-
-
case EIO:
-
/* Could ignore EIO, see spec. */
-
-
/* fall through */
-
-
default:
-
errno_exit("VIDIOC_DQBUF");
-
}
-
}
-
-
for (i = 0; i < n_buffers; ++i)
-
if (buf.m.userptr == (unsigned long)buffers[i].start
-
&& buf.length == buffers[i].length)
-
break;
-
-
assert(i < n_buffers);
-
-
process_image((void *)buf.m.userptr, buf.bytesused);
-
-
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
-
errno_exit("VIDIOC_QBUF");
-
break;
-
}
-
-
return 1;
-
}
-
-
static void mainloop(void)
-
{
-
unsigned int count;
-
unsigned int loopIsInfinite = 0;
-
-
if (frame_count == 0) loopIsInfinite = 1; //infinite loop
-
count = frame_count;
-
-
while ((count-- > 0) || loopIsInfinite) {
-
for (;; ) {
-
fd_set fds;
-
struct timeval tv;
-
int r;
-
-
FD_ZERO(&fds);
-
FD_SET(fd, &fds);
-
-
/* Timeout. */
-
tv.tv_sec = 2;
-
tv.tv_usec = 0;
-
-
r = select(fd + 1, &fds, NULL, NULL, &tv);
-
-
if (-1 == r) {
-
if (EINTR == errno)
-
continue;
-
errno_exit("select");
-
}
-
-
if (0 == r) {
-
fprintf(stderr, "select timeoutn");
-
exit(EXIT_FAILURE);
-
}
-
-
if (read_frame())
-
break;
-
/* EAGAIN - continue select loop. */
-
}
-
}
-
}
-
-
static void stop_capturing(void)
-
{
-
enum v4l2_buf_type type;
-
-
switch (io) {
-
case IO_METHOD_READ:
-
/* Nothing to do. */
-
break;
-
-
case IO_METHOD_MMAP:
-
case IO_METHOD_USERPTR:
-
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
-
errno_exit("VIDIOC_STREAMOFF");
-
break;
-
}
-
}
-
-
static void start_capturing(void)
-
{
-
unsigned int i;
-
enum v4l2_buf_type type;
-
-
switch (io) {
-
case IO_METHOD_READ:
-
/* Nothing to do. */
-
break;
-
-
case IO_METHOD_MMAP:
-
for (i = 0; i < n_buffers; ++i) {
-
struct v4l2_buffer buf;
-
-
CLEAR(buf);
-
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
buf.memory = V4L2_MEMORY_MMAP;
-
buf.index = i;
-
-
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
-
errno_exit("VIDIOC_QBUF");
-
}
-
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
-
errno_exit("VIDIOC_STREAMON");
-
break;
-
-
case IO_METHOD_USERPTR:
-
for (i = 0; i < n_buffers; ++i) {
-
struct v4l2_buffer buf;
-
-
CLEAR(buf);
-
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
buf.memory = V4L2_MEMORY_USERPTR;
-
buf.index = i;
-
buf.m.userptr = (unsigned long)buffers[i].start;
-
buf.length = buffers[i].length;
-
-
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
-
errno_exit("VIDIOC_QBUF");
-
}
-
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
-
errno_exit("VIDIOC_STREAMON");
-
break;
-
}
-
}
-
-
static void uninit_device(void)
-
{
-
unsigned int i;
-
-
switch (io) {
-
case IO_METHOD_READ:
-
free(buffers[0].start);
-
break;
-
-
case IO_METHOD_MMAP:
-
for (i = 0; i < n_buffers; ++i)
-
if (-1 == munmap(buffers[i].start, buffers[i].length))
-
errno_exit("munmap");
-
break;
-
-
case IO_METHOD_USERPTR:
-
for (i = 0; i < n_buffers; ++i)
-
free(buffers[i].start);
-
break;
-
}
-
-
free(buffers);
-
}
-
-
static void init_read(unsigned int buffer_size)
-
{
-
buffers = calloc(1, sizeof(*buffers));
-
-
if (!buffers) {
-
fprintf(stderr, "Out of memoryn");
-
exit(EXIT_FAILURE);
-
}
-
-
buffers[0].length = buffer_size;
-
buffers[0].start = malloc(buffer_size);
-
-
if (!buffers[0].start) {
-
fprintf(stderr, "Out of memoryn");
-
exit(EXIT_FAILURE);
-
}
-
}
-
-
static void init_mmap(void)
-
{
-
struct v4l2_requestbuffers req;
-
-
CLEAR(req);
-
-
req.count = 4;
-
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
req.memory = V4L2_MEMORY_MMAP;
-
-
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
-
if (EINVAL == errno) {
-
fprintf(stderr, "%s does not support "
-
"memory mappingn", dev_name);
-
exit(EXIT_FAILURE);
-
} else {
-
errno_exit("VIDIOC_REQBUFS");
-
}
-
}
-
-
if (req.count < 2) {
-
fprintf(stderr, "Insufficient buffer memory on %sn",
-
dev_name);
-
exit(EXIT_FAILURE);
-
}
-
-
buffers = calloc(req.count, sizeof(*buffers));
-
-
if (!buffers) {
-
fprintf(stderr, "Out of memoryn");
-
exit(EXIT_FAILURE);
-
}
-
-
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
-
struct v4l2_buffer buf;
-
-
CLEAR(buf);
-
-
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
buf.memory = V4L2_MEMORY_MMAP;
-
buf.index = n_buffers;
-
-
if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
-
errno_exit("VIDIOC_QUERYBUF");
-
-
buffers[n_buffers].length = buf.length;
-
buffers[n_buffers].start =
-
mmap(NULL /* start anywhere */,
-
buf.length,
-
PROT_READ | PROT_WRITE /* required */,
-
MAP_SHARED /* recommended */,
-
fd, buf.m.offset);
-
-
if (MAP_FAILED == buffers[n_buffers].start)
-
errno_exit("mmap");
-
}
-
}
-
-
static void init_userp(unsigned int buffer_size)
-
{
-
struct v4l2_requestbuffers req;
-
-
CLEAR(req);
-
-
req.count = 4;
-
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
req.memory = V4L2_MEMORY_USERPTR;
-
-
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
-
if (EINVAL == errno) {
-
fprintf(stderr, "%s does not support "
-
"user pointer i/on", dev_name);
-
exit(EXIT_FAILURE);
-
} else {
-
errno_exit("VIDIOC_REQBUFS");
-
}
-
}
-
-
buffers = calloc(4, sizeof(*buffers));
-
-
if (!buffers) {
-
fprintf(stderr, "Out of memoryn");
-
exit(EXIT_FAILURE);
-
}
-
-
for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
-
buffers[n_buffers].length = buffer_size;
-
buffers[n_buffers].start = malloc(buffer_size);
-
-
if (!buffers[n_buffers].start) {
-
fprintf(stderr, "Out of memoryn");
-
exit(EXIT_FAILURE);
-
}
-
}
-
}
-
-
static void init_device(void)
-
{
-
struct v4l2_capability cap;
-
struct v4l2_cropcap cropcap;
-
struct v4l2_crop crop;
-
struct v4l2_format fmt;
-
unsigned int min;
-
-
if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
-
if (EINVAL == errno) {
-
fprintf(stderr, "%s is no V4L2 devicen",
-
dev_name);
-
exit(EXIT_FAILURE);
-
} else {
-
errno_exit("VIDIOC_QUERYCAP");
-
}
-
}
-
-
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
-
fprintf(stderr, "%s is no video capture devicen",
-
dev_name);
-
exit(EXIT_FAILURE);
-
}
-
-
switch (io) {
-
case IO_METHOD_READ:
-
if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
-
fprintf(stderr, "%s does not support read i/on",
-
dev_name);
-
exit(EXIT_FAILURE);
-
}
-
break;
-
-
case IO_METHOD_MMAP:
-
case IO_METHOD_USERPTR:
-
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
-
fprintf(stderr, "%s does not support streaming i/on",
-
dev_name);
-
exit(EXIT_FAILURE);
-
}
-
break;
-
}
-
-
-
/* Select video input, video standard and tune here. */
-
-
-
CLEAR(cropcap);
-
-
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
-
if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
-
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
crop.c = cropcap.defrect; /* reset to default */
-
-
if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
-
switch (errno) {
-
case EINVAL:
-
/* Cropping not supported. */
-
break;
-
default:
-
/* Errors ignored. */
-
break;
-
}
-
}
-
} else {
-
/* Errors ignored. */
-
}
-
-
-
CLEAR(fmt);
-
-
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
fprintf(stderr, "Force Format %dn", force_format);
-
if (force_format) {
-
if (force_format==2){
-
fmt.fmt.pix.width = 1920;
-
fmt.fmt.pix.height = 1080;
-
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_H264;
-
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
-
}
-
else if(force_format==1){
-
fmt.fmt.pix.width = 640;
-
fmt.fmt.pix.height = 480;
-
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
-
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
-
}
-
-
if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
-
errno_exit("VIDIOC_S_FMT");
-
-
/* Note VIDIOC_S_FMT may change width and height. */
-
} else {
-
/* Preserve original settings as set by v4l2-ctl for example */
-
if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
-
errno_exit("VIDIOC_G_FMT");
-
}
-
-
/* Buggy driver paranoia. */
-
min = fmt.fmt.pix.width * 2;
-
if (fmt.fmt.pix.bytesperline < min)
-
fmt.fmt.pix.bytesperline = min;
-
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
-
if (fmt.fmt.pix.sizeimage < min)
-
fmt.fmt.pix.sizeimage = min;
-
-
switch (io) {
-
case IO_METHOD_READ:
-
init_read(fmt.fmt.pix.sizeimage);
-
break;
-
-
case IO_METHOD_MMAP:
-
init_mmap();
-
break;
-
-
case IO_METHOD_USERPTR:
-
init_userp(fmt.fmt.pix.sizeimage);
-
break;
-
}
-
}
-
-
static void close_device(void)
-
{
-
if (-1 == close(fd))
-
errno_exit("close");
-
-
fd = -1;
-
}
-
-
static void open_device(void)
-
{
-
struct stat st;
-
-
if (-1 == stat(dev_name, &st)) {
-
fprintf(stderr, "Cannot identify '%s': %d, %sn",
-
dev_name, errno, strerror(errno));
-
exit(EXIT_FAILURE);
-
}
-
-
if (!S_ISCHR(st.st_mode)) {
-
fprintf(stderr, "%s is no devicen", dev_name);
-
exit(EXIT_FAILURE);
-
}
-
-
fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
-
-
if (-1 == fd) {
-
fprintf(stderr, "Cannot open '%s': %d, %sn",
-
dev_name, errno, strerror(errno));
-
exit(EXIT_FAILURE);
-
}
-
}
-
复制代码
编写好的代码,可以参照例程工程里的Makefile文件,编写编译脚本,进入GL2编译环境下,进行编译调试(省去调试修改代码xxx字)最后把编译好的程序上传到开发板上,启动运行如下;
v4l2capture-m save -d /dev/video0 -t usb -F YUYV -w 640 -h 480 -f 15 -o/home/root/test.yuv -n 300
参数含义如下:
-d 选择采集设备
-t usb采集
-F 采集视频色彩数据组织格式,YUYV对应的ffmpeg里就是 YUV422格式
-w 采集视频画面长度,这个宽度必须是采集设备支持的规格表里的参数
-h 采集视频画面高度,同上需要满足视频规格标准
-f 采集帧率,必须是视频采集支持的规格
-o 输出文件
-n 采集帧数
图14
采集完成后,将yuv数据上传到windows下,使用GL2工具包下的tools目录下的YUV Player.exe,播放,因为yuv文件不记录视频的长宽,以及格式信息,所以需要在yuv播放器中需要配置正确的尺寸,和视频格式信息,才能正确的播出。
图15
文末放了两段视频,一段是是
手机拍摄,拍摄启动采集程序和移动摄像头的过程。另外一段视频为采集到的yuv数据上传到Ubuntu主机后,经过转换和编码生成mp4,可以看摄像头到实际采集到的画面效果。
六、视频采集开发测评总结 通过对Logitech C270 摄像头的视频采集开发测试来看,RZGL2开发板支持视频采集功能很完善,支持V4L2,FFMPEG这样常用的音视频处理框架,使得很多音视频应用移植起来成为可能,也比较简单。
从采集到的视频看,采集速率较高,支持视频的规格也很广。这为后面开发提供很好的基础,在后面较长时间(>60分钟)的视频采集测试中,采集程序和系统运行非常稳定,没有出现异常中断等现象,并且在持续视频采集中,触摸CPU感觉升温不明显,这也可以看该处理优良的功耗表现。
摄像头采集到的视频回放,开发板体验视频详见作者原帖子