主题 : Linux下V4l2接口摄像头图像数据采集问题 复制链接 | 浏览器收藏 | 打印
级别: 新手上路
UID: 28880
精华: 0
发帖: 13
金钱: 65 两
威望: 13 点
贡献值: 0 点
综合积分: 26 分
注册时间: 2010-09-18
最后登录: 2011-03-02
楼主  发表于: 2010-10-05 20:30

 Linux下V4l2接口摄像头图像数据采集问题

最近在忙基于Linux下的video for linux 2 接口的应用程序设计,程序已经能在PC机上跑通,并能顺利采集摄像头图像数据。但是,在移到板子上(S3C2440),出现了一些问题,前面部分问题已经被我自己解决好,但是还有一个问题搞不清楚。闲话少说,问题如下:

程序在调用ioctl()函数向驱动申请内存 如下:

struct v4l2_requestbuffers  req;


if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
  return -1;
}

可以看到,这里传给 ioctl() 函数的参数有:设备fd, 命令VIDIOC_REQBUFS , buffer的结构体req三个。我们知道,设备fd就是摄像头设备文件,命令VIDIOC_REQBUFS 是表示让ioctl这个系统调用函数分配缓存空间,结构体req里一般有3个成员:

1: 要申请buffer的数量count   // 缓存数量,也就是说在缓存队列里保持多少张照片

2:     数据流类型             //  必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE

3:    memory的类型       // V4L2_MEMORY_MMAP 或 V4L2_MEMORY_USERPTR,这里用的mmap方式

        这段程序没有传给其每个buffer的大小,程序分配给buffer的内存大小是根据什么来的呢?我在PC上之所以在这一点上能成功,是因为在PC下的,被ioctl()函数分配到的每个buffer大小 刚好是我的摄像头的图像的大小640×480×2,但是同样的程序,对于同样的摄像头,在板子上,被分配的buffer大小不是这个值,但也是定值,就这里弄不明白了。望前辈指导!

       接下来是内存映射,将缓存映射到用户空间,但是缓存空间分配工作是上面的程序做的,下面的这段 ioctl(VIDIOC_QUERYBUF )只是根据上面分配的缓存个数来进行映射。学习下面的这段程序可以发现,下面的 ioctl() 函数根据传进来的type ,memory ,index 参数,返回缓存分配的其他信息到buf结构体其他成员变量中(如我最头痛的buf.length ),以给后面的mmap()函数用,进行映射。

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;
    // 读取缓存
    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);


          我曾尝试在ioctl(VIDIOC_QUERYBUF )前,先给buf.length 赋值(如 buf.length = 640*480*2;),但是经过
ioctl(VIDIOC_QUERYBUF )后,buf.length 值被设成了其他值,这个值,也就是上面我的问题所在。



另附:(一些结构体)

struct v4l2_requestbuffers
{
    __u32               count;  // 缓存数量,也就是说在缓存队列里保持多少张照片
    enum v4l2_buf_type  type;   // 数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
    enum v4l2_memory    memory; // V4L2_MEMORY_MMAP 或 V4L2_MEMORY_USERPTR
    __u32               reserved[2];
};
级别: 新手上路
UID: 16042
精华: 0
发帖: 47
金钱: 245 两
威望: 49 点
贡献值: 0 点
综合积分: 94 分
注册时间: 2010-03-13
最后登录: 2012-03-23
1楼  发表于: 2010-10-12 21:41
buffer的大小的确不总是和解析度对应的,个人猜测跟工作模式有关。
比如在YUYV模式下,每帧的大小是固定的,这时返回的可能可以是定值。但是若工作在MJPG模式下,每帧的大小根据图像复杂度的不同会有变化,如果固定一个值很可能造成溢出,为了避免这种情况发生,只能尽量分配一个足够大的缓存。而这个“最大值”应该是由驱动定义或者摄像头硬件芯片返回得到的。
以上仅为猜测,想确定答案你应该去linux-uvc-devel的邮件表去问一下,我的工作集中在算法部分,所以对这个问题也没有深究。
级别: 新手上路
UID: 28880
精华: 0
发帖: 13
金钱: 65 两
威望: 13 点
贡献值: 0 点
综合积分: 26 分
注册时间: 2010-09-18
最后登录: 2011-03-02
2楼  发表于: 2010-10-13 13:06

 回 1楼(gdglacier) 的帖子

恩,谢谢
级别: 新手上路
UID: 63351
精华: 0
发帖: 29
金钱: 150 两
威望: 30 点
贡献值: 0 点
综合积分: 58 分
注册时间: 2012-02-18
最后登录: 2017-03-04
3楼  发表于: 2012-03-02 09:57
朋友,你好啊!我怎么映射不了到用户空间呢?到这里   if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) //映射用户空间,,总是没成功返回 ,,怎么回事呢?求救啊

/*为设备申请缓冲区*/
        struct v4l2_requestbuffers req;

        CLEAR (req);
    /*缓冲区内缓冲帧的数目*/
        req.count               = 1;
        /*捕捉视频格式*/
        req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        /*采用内存映射方式*/
        req.memory              = V4L2_MEMORY_MMAP;
        
        ff = ioctl (fd, VIDIOC_REQBUFS, &req); //申请缓冲,count是申请的数量
        /*申请缓冲区失败*/
        if(ff == -1)
            {
                    printf("failture VIDIOC_REQBUFS\n");
                    exit (EXIT_FAILURE);    
            }
        printf("\n\n VIDIOC_S_REQBUFS SUCCESS !\n");


        if (req.count < 1)
        /*申请的缓冲区不足*/
           {
                   printf("Insufficient buffer memory,exit,please try again ...\n");
                   exit(EXIT_FAILURE);
           }
        
        /*动态申请内存空间,用于映射设备缓冲区到用户空间*/
        //buffers = calloc (req.count, sizeof (*buffers));
        buffers = (struct buffer*)calloc (req.count, sizeof (*buffers));
        if (!buffers)
            {
                    printf ( "\nOut of memory/n");
                    exit (EXIT_FAILURE);
            }
            
        struct v4l2_buffer buf;//驱动中的一帧
        for (n_buffers = 0; n_buffers < req.count; n_buffers++)

        {

          // struct v4l2_buffer buf;   //驱动中的一帧

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

           buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;

           buf.memory      = V4L2_MEMORY_MMAP;

           buf.index       = n_buffers;

            /*获取申请到得用于缓冲帧的缓冲区的地址和长度*/
           if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) //映射用户空间
               {
                         printf ("\nVIDIOC_QUERYBUF Error\n");
                         exit (EXIT_FAILURE);    
               }
             printf ("\nVIDIOC_QUERYBUF SUCCESS ...\n");
        
           buffers[n_buffers].length = buf.length;

           buffers[n_buffers].start =

           mmap (NULL /* start anywhere */,    //通过mmap建立映射关系

            buf.length,

            PROT_READ | PROT_WRITE /* required */,

            MAP_SHARED /* recommended */,

            fd, buf.m.offset);

           if (MAP_FAILED == buffers[n_buffers].start)
                    {
                            printf ("\nmmap failed\n");
                            exit (EXIT_FAILURE);    
                    }
            printf ("\n mmap Success...\n");

         }