您的位置:首页 > 编程语言 > Qt开发

在QT中的摄像头程序

2014-04-25 10:47 211 查看
#include "hello.h"

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <fcntl.h>

#include <unistd.h>

#include <errno.h>

#include <time.h>

#include <sys/ioctl.h>

#include <sys/mman.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <linux/videodev2.h>

#include <qapplication.h>

#include <qlabel.h>

#include <qpushbutton.h>

#include <qtooltip.h>

#include <qpainter.h>

#include <qimage.h>

#define WIDTH 160

#define HEIGHT 120

#define FRAME_COUNT 1 //?建?????

#define FRAME_IDLE 60

typedef struct VideoBuffer //定义一个结构体来映射每个缓冲帧

{

void *start;

size_t length;

}VideoBuffer;

static int fd;

static VideoBuffer *buffers = NULL;

static unsigned int numBufs = 0;

static int v4l2_get_capability(void)//读取设备拥有的功能

{

struct v4l2_capability cap;

if (::ioctl (fd, VIDIOC_QUERYCAP, &cap) == -1)

{

printf("Get camara capability is fail.\n");

return -1;

}

printf("camara driver: %s\n", cap.driver);

printf("camara card: %s\n", cap.card);

printf("camara bus info: %s\n", cap.bus_info);

printf("camara version: %d\n", cap.version);

if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) //是否支持图像获取

{

printf("Camara does not support VIDEO CAPTURE.\n");

return -1;

}

if (!(cap.capabilities & V4L2_CAP_STREAMING)) //I/O的读写控制流

{

printf("Camara does not support STREAMING.\n");

return -1;

}

return 0;

/*

相关结构体:

structv4l2_capability

{

__u8 driver[16]; // 驱动名字

__u8 card[32]; // 设备名字

__u8bus_info[32]; // 设备在系统中的位置

__u32 version; // 驱动版本号

__u32capabilities; // 设备支持的操作

__u32reserved[4]; // 保留字段

};

capabilities 常用值:

V4L2_CAP_VIDEO_CAPTURE // 是否支持图像获取

Values for 'capabilities' field //内核驱动中定义的视频驱动的能力

#define V4L2_CAP_VIDEO_CAPTURE 0x00000001 /* Is a video capture device

#define V4L2_CAP_VIDEO_OUTPUT 0x00000002 /* Is a video output device

#define V4L2_CAP_VIDEO_OVERLAY 0x00000004 /* Can do video overlay

#define V4L2_CAP_VBI_CAPTURE 0x00000010 /* Is a raw VBI capture device

#define V4L2_CAP_VBI_OUTPUT 0x00000020 /* Is a raw VBI output device

#define V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040 /* Is a sliced VBI capture device

#define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 /* Is a sliced VBI output device

#define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture

#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200 /* Can do video output overlay

#define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek

#define V4L2_CAP_RDS_OUTPUT 0x00000800 /* Is an RDS encoder

#define V4L2_CAP_TUNER 0x00010000 /* has a tuner

#define V4L2_CAP_AUDIO 0x00020000 /* has audio support

#define V4L2_CAP_RADIO 0x00040000 /* is a radio device *

#define V4L2_CAP_MODULATOR 0x00080000 /* has a modulator *

#define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls *

#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O *

#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls *

} */

static int v4l2_set_fmt(void)//设置视频帧格式,包括帧的点阵格式,宽度和高度

{

struct v4l2_format fmt;

memset(&fmt, 0, sizeof(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_MJPEG;

fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

if (::ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)//检查是否支持V4L2_PIX_FMT_MJPEG 格式

{

printf("Camara set fmt is errror.\n");

return -1;

}

return 0;

}

/*

VIDIOC_TRY_FMT

int ioctl(intfd, int request, struct v4l2_format *argp);

structv4l2_format

{

enumv4l2_buf_type type;// 帧类型,应用程序设置

union fmt

{

structv4l2_pix_format pix;// 视频设备使用

structv4l2_window win;

structv4l2_vbi_format vbi;

structv4l2_sliced_vbi_format sliced;

__u8raw_data[200];

};

};

structv4l2_pix_format

{

__u32 width; // 帧宽,单位像素

__u32 height; // 帧高,单位像素

__u32pixelformat; // 帧格式

enum v4l2_fieldfield;

__u32bytesperline;

__u32 sizeimage;

enumv4l2_colorspace colorspace;

__u32 priv;

};

*/

static int v4l2_set_memory(void)

{

struct v4l2_requestbuffers req;

enum v4l2_buf_type type; //enum v4l2_buf_type type; //数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE

struct v4l2_buffer buf;

// 向设备申请缓冲区

/*

structv4l2_requestbuffers

{

__u32 count; // 缓冲区内缓冲帧的数目

enumv4l2_buf_type type; // 缓冲帧数据格式

enum v4l2_memorymemory; // 区别是内存映射还是用户指针方式

__u32 reserved[2];

};

*/

memset(&req, 0, sizeof (req));

req.count = FRAME_COUNT;////缓存数量,也就是说在缓存队列里保持多少张照片 申请一个拥有FRAME_COUNT个缓冲帧的缓冲区

req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

req.memory = V4L2_MEMORY_MMAP;

if (::ioctl(fd, VIDIOC_REQBUFS, &req) < 0)

{

printf("VIDIOC_REQBUFS failed\n");

return -1;

}

// 使用VIDIOC_REQBUFS,我们获取了req.count个缓存,下一步通过调用VIDIOC_QUERYBUF命令来获取这些缓存的地址,

//然后使用mmap函数转换成应用程序中的绝对地址,最后把这段缓存放入缓存队列:

// 获取缓冲帧的地址,长度:

/*

struct v4l2_buffer

{

__u32 index; //buffer 序号

enumv4l2_buf_type type; //buffer 类型

__u32 byteused; //buffer 中已使用的字节数

__u32 flags; // 区分是MMAP 还是USERPTR

enum v4l2_fieldfield;

struct timevaltimestamp;// 获取第一个字节时的系统时间

structv4l2_timecode timecode;

__u32 sequence;// 队列中的序号

enum v4l2_memorymemory;//IO 方式,被应用程序设置

union m

{

__u32 offset;// 缓冲帧地址,只对MMAP 有效

unsigned longuserptr;

};

__u32 length;// 缓冲帧长度

__u32 input;

__u32 reserved;

};

*/

//将FRAME_COUNT个已申请到的缓冲帧映射到应用程序,用buffers 指针记录。

buffers = (VideoBuffer *)calloc(req.count, sizeof(*buffers));

// 映射

for (numBufs = 0; numBufs < req.count; numBufs++)

{

memset(&buf, 0, sizeof(buf));

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

buf.index = numBufs;

// 查询序号为numBufs的缓冲区,得到其起始物理地址和大小

if (::ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) // 读取缓存

return -1;

buffers[numBufs].length = buf.length;

// 转换成相对地址

//映射内存

buffers[numBufs].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);//返回所分配的内存空间的首地址。

/*

void *mmap(void*addr, size_t length, int prot, int flags, int fd, off_t offset);

//addr 映射起始地址,一般为NULL ,让内核自动选择

//length 被映射内存块的长度

//prot 标志映射后能否被读写,其值为PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE

//flags 确定此内存映射能否被其他进程共享,MAP_SHARED,MAP_PRIVATE

//fd,offset, 确定被映射的内存地址

返回成功映射后的地址,不成功返回MAP_FAILED ((void*)-1);*/

if (buffers[numBufs].start == MAP_FAILED)

return -1;

//VIDIOC_QBUF// 把帧放入队列

//VIDIOC_DQBUF// 从队列中取出帧

if (::ioctl(fd, VIDIOC_QBUF, &buf) == -1)// 放入缓存队列

return -1;

}

// 在开始之前,还应当把缓冲帧放入缓冲队列:

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (::ioctl (fd, VIDIOC_STREAMON, &type) < 0) //开始视屏流数据的采集

{

printf("VIDIOC_STREAMON error\n");

return -1;

}

return 0;

}

//获取一帧并处理

static int v4l2_read_pic(int index)

{

struct v4l2_buffer buf;

memset(&buf,0,sizeof(buf));

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

buf.index = index;

//VIDIOC_DQBUF 从队列中取出帧

if (::ioctl(fd, VIDIOC_DQBUF, &buf) == -1)

return -1;

/*图像处理

process_image(buffers[buf.index].start); */

//将取出的缓冲帧放回缓冲区

if (::ioctl(fd, VIDIOC_QBUF, &buf) == -1)

return -1;

return 0;

}

static int v4l2_init(const char *dev_name)

{

// 鎵撳紑video device

fd = ::open(dev_name, O_RDWR);//打开视屏设备文件

if (fd == -1)

{

printf("open camera is fail.\n");

return -1;

}

//查询设备属性

if (v4l2_get_capability())

return -1;

// 设置帧格式

if (v4l2_set_fmt())

return -1;

// 申请和管理缓冲区

if (v4l2_set_memory())

return -1;

return 0;

}

MyHelloForm::MyHelloForm( QWidget* parent, const char* name, WFlags fl)

:HelloBaseForm(parent, name, fl)

{

if (v4l2_init("/dev/video0"))

{

printf("v4l2 init is error!\n");

emit(close_signal());

}

connect (this, SIGNAL(close_signal()), this, SLOT(camara_quit()));

connect (this, SIGNAL(quit_signal()), qApp, SLOT(quit()));

connect (CloseButton, SIGNAL(clicked()), this, SLOT(camara_quit()));

connect (SnapButton, SIGNAL(clicked()), this, SLOT(camara_snap()));

timer = new QTimer(this);

connect (timer, SIGNAL(timeout()), this, SLOT(showMe()));

timer->start(FRAME_IDLE);

}

MyHelloForm::~MyHelloForm()

{

}

void MyHelloForm::showMe()

{

QPainter painter(this);

QImage image;

if (v4l2_read_pic(0))

return;

image.loadFromData((uchar *)buffers[0].start, buffers[0].length, "JPEG");

painter.drawImage(40, 30, image, 0, 0, WIDTH, HEIGHT);//drawImage是QPainter的一个方法,

// 这里的意思是将image对象放到窗口的(40,30)位置,亦即左上角,最后两个数字是指定的大小。

}

void MyHelloForm::camara_snap()

{

int pic_fd, i;

time_t t = time(NULL);

char name[50], *pic_name, *dir_name = "/root/Documents/";

size_t len, len1;

timer->stop();

pic_name = ctime(&t);//我们可以通过asctime()函数和ctime()函数将时间以固定的格式显示出来,两者的返回值都是char*型的字符串

len = strlen(pic_name);

len1 = strlen(dir_name);

dir_name = strcpy(name, dir_name);

for (i=0; i<len-1; i++)

dir_name[len1+i] = pic_name[i];

dir_name[len1+len-1] = '\0';

dir_name = strcat(dir_name, ".jpg");

printf("dir_name: %s\n", dir_name);

pic_fd = ::open(dir_name, O_RDWR | O_CREAT, S_IRWXU);

if (pic_fd == -1)

{

printf("save jpg file fail\n");

return;

}

::write(pic_fd, buffers[0].start, buffers[0].length);

::close(pic_fd);

sleep(1);

timer->start(FRAME_IDLE);

}

void MyHelloForm::camara_quit()

{

munmap(buffers[0].start, FRAME_COUNT*buffers[0].length);

::close(fd);

timer->stop();

emit(quit_signal());

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: