v4l2的input/output 错误问题
我将ZC301PLH摄像头用在S3C2440上,内核版本为2.6.33,以前使用ZC301PL的摄像头,工作正常,但是换上ZC301PLH摄像头后,在调用ioctl (fd, VIDIOC_DQBUF, &buf)读取帧缓冲的时候出现了 input/output 错误,不知道什么原因,其他配置没有报错,并读出来对比过,和写入配置相同。代码如下:
这段代码是网上一位朋友提供的,我的程序就是用这段代码改的,代码太多,贴出来很不方便,就先贴这位朋友的代码给大家做参考,里面有一些小错误,请忽略,大致流程是一样的。
#define VIDEO_DEVICE "/dev/video0"
#define pictureNumber 4
#define SIZE 32768
typedef struct VideoBuffer
{
void *start;
int offset;
size_t length;
}VideoBuffer;
extern char testbuf[SIZE];//SIZE大小有下面的设置决定,此处为32768,所有图像相同大小
extern VideoBuffer *buffers;//描述帧缓冲的映射地址
extern int SocketFd;//基于udp协议
extern int videofd;//摄像头文件描述符(本实验中为open /dev/video1的返回值)
void cap_pic(char *start, unsigned int length,char *filename)
{
FILE * file_fd;
if((file_fd = fopen(filename,"wb"))<0)
{
perror("Open file error\n");
exit(1);
}
if(length==0)
{
DebugPrintf("error:pic length = 0\n");
}
else
{
fwrite(start, length, 1, file_fd);
}
DebugPrintf("pic %s capture over!\n",filename);
fclose(file_fd);
}
int VideoInit(void)
{
struct v4l2_requestbuffers req;
struct v4l2_buffer buf;
videofd=open(VIDEO_DEVICE,O_RDWR);
if (videofd==-1){
printf("open the file of video1 ** failed\n");
return -1;
}
if(set_attribute(videofd) < 0)//设置视频格式
return -1;
if( (buffer_request(videofd,&req)) < 0) //申请桢缓冲
return -1;
CatBegin(videofd);//开始采集
buffers = calloc(req.count,sizeof(VideoBuffer));
AddrReverse(&buffers,req.count,videofd); //从内核地址到用户地址的映射
}
int set_attribute(int fd)
{
int ret;
struct v4l2_format fmt;
memset(&fmt,0,sizeof(struct v4l2_format));
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 320;
fmt.fmt.pix.height = 240;//320*240(图像大小与此有关,此处为32768)
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;//得到jpeg格式图像
ret=ioctl(fd, VIDIOC_S_FMT, &fmt);
if(ret<0){
printf("in set_attribute ,ioctl error !\n");
return -1;
}
}
int buffer_request(int fd,struct v4l2_requestbuffers *req)//
{
int ret;
memset(req, 0, sizeof (struct v4l2_requestbuffers));
req->count = pictureNumber;//一般为4,申请4个帧缓冲,pictureNumber为头文件里的宏定义
req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req->memory = V4L2_MEMORY_MMAP;
ret=ioctl(fd, VIDIOC_REQBUFS,req);
if(ret<0){
printf("set buffer failed\n");
return -1;
}
return 0;
}
int PictureGet(int fd)
{
static int index = 0;//用static修饰,为的是每调用一次,index保留上次值,进而可以依次读取四个桢缓冲
struct v4l2_buffer buf;
int ret;
char *ptcur = buffers[index].start;//读帧缓冲通过映射到用户空间来读,buffers [index].start中存放的是四个映射到用户空间地址的指针,指针分别间接指向四个帧缓冲
printf("......................frame %d\n",index);
buf.index = index;//本次调用读标志为index的帧缓冲,与用户空间相对应
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
出错的地方
if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0)//出队列
{
perror("VIDIOC_DQBUF failed.\n");
printf("fd = %d\n",fd);
}
memcpy(testbuf,ptcur,sizeof(testbuf));//将该帧数据复制到全局的缓冲区testbuf中,testbuf大小32768
cap_pic(testbuf,sizeof(testbuf),"pic.jpg");
ret=ioctl(fd, VIDIOC_QBUF, &buf);//入队列,此时标志为index的帧缓冲数据被刷新,如不入队,得不到新数据
if(ret< 0) {
perror("ioctl");
printf("VIDIOC_QBUF error\n");
}
index++;
if (index == pictureNumber)
index = 0;
}
int AddrReverse(VideoBuffer **buffers,int reqcount,int fd)
{
int numBufs;
struct v4l2_buffer buf;
int ret;
for (numBufs = 0; numBufs < reqcount; numBufs++)//循环reqcount次,分别对reqcount个帧缓冲映射
{
memset( &buf, 0, sizeof(buf) );
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = numBufs;//
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0){
printf("VIDIOC_QUERYBUF error\n");
return -1;
}
(*buffers + numBufs)->length = buf.length;
(*buffers +numBufs)->offset = (size_t)(buf.m.offset);
(*buffers + numBufs)->start = mmap((*buffers + numBufs)->start, buf.length,PROT_READ | PROT_WRITE, MAP_SHARED, fd,(*buffers + numBufs)->offset);
if ((*buffers + numBufs)->start == MAP_FAILED){
perror("buffers error\n");
return -1;
}
ret=ioctl(fd, VIDIOC_QBUF, &buf);//将帧缓冲入队
if(ret< 0) {
printf("ret3=%d\n",ret);
printf("VIDIOC_QBUF error\n");
return -1;
}
}
return 0;
}
int CatBegin(int fd)
{
int ret;
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret=ioctl (fd, VIDIOC_STREAMON, &type); //程序运行到这里四个缓冲区里面就有数据了
perror("ioctl");
if(ret<0){
printf("VIDIOC_STREAMON error\n");
return -1;
}
}
int main(int argc,char **argv)
{
VideoInit();
PictureGet(videofd);
usleep(1000);
return 0;
}