常用的命令标识符和结构体
// 在 include/linux/videodev2.h 中定义
VIDIOC_REQBUFS 分配内存;
VIDIOC_QUERYBUF 把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址;
VIDIOC_QUERYCAP 查询驱动功能;
VIDIOC_ENUM_FMT 获取当前驱动支持的视频格式;
VIDIOC_S_FMT 设置当前驱动的视频捕获格式;
VIDIOC_G_FMT 读取当前驱动的视频捕获格式;
VIDIOC_TRY_FMT 验证当前驱动的显示格式;
VIDIOC_CROPCAP 查询驱动的修剪功能;
VIDIOC_S_CROP 设置视频信号的边框;
VIDIOC_G_CROP 读取视频信号的边框;
VIDIOC_QBUF 把数据从缓存中读取出来;
VIDIOC_DQBUF 把数据放回缓存队列;
VIDIOC_STREAMON 开始视频显示函数;
VIDIOC_STREAMOFF 结束视频显示函数;
VIDIOC_QUERYSTD 检查当前视频设备支持的标准,例如PAL或NTSC;
// 在include/linux/videodev2.h中定义
struct v4l2_requestbuffers //申请帧缓冲,对应命令VIDIOC_REQBUFS
struct v4l2_capability //视频设备的功能,对应命令VIDIOC_QUERYCAP
struct v4l2_input //视频输入信息,对应命令VIDIOC_ENUMINPUT
struct v4l2_standard //视频的制式,比如PAL,NTSC,对应命令 VIDIOC_ENUMSTD
struct v4l2_format //帧的格式,对应命令VIDIOC_G_FMT、VIDIOC_S_FMT等
struct v4l2_buffer //驱动中的一帧图像缓存,对应命令VIDIOC_QUERYBUF
struct v4l2_crop //视频信号矩形边
v4l2_std_id //视频制式
应用层流程图
详细使用可查看:https://www.dandroid.cn/21599
下面是我写的一个事例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <asm/types.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
/* TCP */
#include "tcp_client.h"
#define CLEAR(x) memset (&(x), 0, sizeof (x))
#define BUFF_NUM 4 // 缓存帧个数
static char * dev_name = "/dev/video2";
static int camera_fd = -1;
static char* buffers[BUFF_NUM];
static unsigned int n_buffers[BUFF_NUM];
/************************************
* 函数名称: camera_init
* 函数功能: 初始化相机
* 参 数: width 相片宽度
* height 相片高度
* 返 回 值:
***********************************/
int camera_init(int width, int height)
{
struct v4l2_capability cap;
struct v4l2_format fmt;
int i;
enum v4l2_buf_type type;
struct v4l2_fmtdesc fmt1;
int ret;
/* 1、打开设备,并查询设备属性 */
camera_fd = open(dev_name, O_RDWR | O_NONBLOCK, 0);
ret = ioctl(camera_fd, VIDIOC_QUERYCAP, &cap);
if(ret < 0)
{
printf("VIDIOC_QUERYCAP error!\n");
return -1;
}
/* 2、设置图像帧格式 */
CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = width;
fmt.fmt.pix.height = height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
//fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
ret = ioctl(camera_fd, VIDIOC_S_FMT, &fmt);
//ret = ioctl(camera_fd, VIDIOC_G_FMT, &fmt);
if(ret < 0)
{
printf("VIDIOC_S_FMT error!\n");
return -1;
}
/* 3、申请缓冲区 */
struct v4l2_requestbuffers req;
CLEAR(req);
req.count = BUFF_NUM;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ret = ioctl(camera_fd, VIDIOC_REQBUFS, &req);
if(ret < 0)
{
printf("VIDIOC_REQBUFS error!\n");
return -1;
}
if(req.count < 1)
printf("Insufficient buffer memory\n");
/* 4、将申请的缓冲帧从内核空间映射到用户空间 */
for(i = 0; i < req.count; i++)
{
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
ret = ioctl(camera_fd, VIDIOC_QUERYBUF, &buf);
if(ret < 0)
{
printf("VIDIOC_QUERYBUF error!\n");
return -1;
}
n_buffers[i] =buf.length;
buffers[i] = mmap(NULL, //start anywhere
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
camera_fd,
buf.m.offset);
if(MAP_FAILED == buffers[i])
{
printf("mmap failed\n");
return -1;
}
memset(buffers[i], 0, n_buffers[i]);
printf("buf.length[%d] = %d\n",i, buf.length);
}
/* 5、将申请的缓冲帧放入队列,并启动数据流 */
for(i = 0; i < req.count; i++)
{
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
ret = ioctl(camera_fd, VIDIOC_QBUF, &buf);
if(ret < 0)
{
printf("VIDIOC_QBUF error!\n");
return -1;
}
}
return 0;
}
/************************************
* 函数名称: camera_start
* 函数功能: 启动捕捉图像数据
* 参 数: 无
* 返 回 值:
***********************************/
int camera_start()
{
int ret;
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl (camera_fd, VIDIOC_STREAMON, &type);
if ( ret < 0 )
{
printf ("VIDIOC_STREAMON failed\n");
return -1;
}
return 0;
}
/************************************
* 函数名称: camera_stop
* 函数功能: 停止捕捉图像数据
* 参 数: 无
* 返 回 值:
***********************************/
int camera_stop()
{
int ret;
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl (camera_fd, VIDIOC_STREAMOFF, &type);
if ( ret < 0 )
{
printf ("VIDIOC_STREAMOFF failed\n");
return -1;
}
return 0;
}
/************************************
* 函数名称: camera_read_frame
* 函数功能: 获取一帧图像数据,并且将取出的帧缓冲重新放回图像输入队列
* 参 数: 存放数据文件的句柄
* 返 回 值:
***********************************/
int camera_read_frame(int fd)
{
struct v4l2_buffer buf;
int ret;
/* 取出缓冲区的数据 */
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(camera_fd, VIDIOC_DQBUF, &buf);
if(ret < 0){
printf("VIDIOC_DQBUF %s\n", __func__);
return -1;
}
/* 将数据写入文件中 */
write(fd,buffers[buf.index], n_buffers[buf.index]);
/* 将取出的帧缓冲重新放回图像输入队列 */
ret = ioctl (camera_fd, VIDIOC_QBUF, &buf);
if(ret < 0){
printf("error: VIDIOC_QBUF %s\n", __func__);
return -1;
}
return 0;
}
/************************************
* 函数名称: capture_an_image
* 函数功能: 捕获一张图片
* 参 数: 无
* 返 回 值:
***********************************/
void capture_an_image(void)
{
int i;
int fd;
while(1)
{
fd_set fds;
int ret;
camera_start();
fd = open("image.jpg",O_CREAT| O_RDWR, 0666);
if(fd < 0)
{
printf("open image.jpg error!\n");
exit(0);
}
FD_ZERO(&fds);
FD_SET(camera_fd, &fds);
ret = select(camera_fd + 1, &fds, NULL, NULL, NULL);
if(ret == -1)
{
if(EINTR == errno)
{
continue;
}
printf("select error!\n");
}
else if(ret == 0)
{
fprintf (stderr, "select timeout\n");
exit (EXIT_FAILURE);
}
camera_read_frame(fd);
camera_stop();
break;
}
close(fd);
}
/************************************
* 函数名称: camera_tcp_send_frame
* 函数功能: 获取一帧图像数据,通过TCP发送,并且将取出的帧缓冲重新放回图像输入队列
* 参 数: 存放数据文件的句柄
* 返 回 值:
***********************************/
int camera_tcp_send_frame()
{
int ret;
int server_socket;
struct v4l2_buffer buf;
camera_start();
server_socket = my_socket();
if(server_socket < 0)
{
perror("socket error\n");
return -1;
}
ret = my_connect(server_socket);
if(ret < 0)
{
perror("connect error\n");
return -1;
}
while(1)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(camera_fd, &fds);
ret = select(camera_fd + 1, &fds, NULL, NULL, NULL);
if(ret == -1)
{
if(EINTR == errno)
{
continue;
}
printf("select error!\n");
}
else if(ret == 0)
{
fprintf (stderr, "select timeout\n");
exit (EXIT_FAILURE);
}
/* 取出缓冲区的数据 */
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(camera_fd, VIDIOC_DQBUF, &buf);
if(ret < 0)
{
printf("ret = %d\n", ret);
printf("%s %s line %d: VIDIOC_DQBUF error\n",__FILE__, __FUNCTION__, __LINE__);
return -1;
}
/* 将数据通过 TCP 发送 */
my_send(server_socket, buffers[buf.index], n_buffers[buf.index]);
/* 将取出的帧缓冲重新放回图像输入队列 */
ret = ioctl (camera_fd, VIDIOC_QBUF, &buf);
if(ret < 0)
{
printf("VIDIOC_QBUF %s\n", __func__);
return -1;
}
}
camera_stop();
my_close(server_socket);
return 0;
}
int main()
{
/* 初始化,传递宽高像素 */
camera_init(1920,1080);
/* 在当前目录下创建 image.jpg 文件,并将捕获到的一帧 jpeg 图像数据存入该文件 */
capture_an_image();
/* 将捕获到的 jpeg 格式的图像数据通过 TCP 发送给服务器进行显示 */
camera_tcp_send_frame();
retrun 0;
}