V4L2 官方 example

/*
 *  V4L2 video capture example
 *
 *  This program can be used and distributed without restrictions.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <getopt.h> /* getopt_long() */

#include <fcntl.h> /* low-level i/o */
#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 <asm/types.h> /* for videodev2.h */
#include <linux/videodev2.h>

#define CLEAR(x) memset (&(x), 0, sizeof (x))

typedef enum
{
    IO_METHOD_READ,
    IO_METHOD_MMAP,
    IO_METHOD_USERPTR,
} io_method;


struct buffer
{
    void *start;
    size_t length;
};


static char *dev_name = NULL;
static io_method io = IO_METHOD_MMAP;
static int fd = -1;
struct buffer *buffers = NULL;
static unsigned int n_buffers = 0;


static void
errno_exit (const char *s)
{
    fprintf (stderr, "%s error %d, %s/n", s, errno, strerror (errno));


    exit (EXIT_FAILURE);
}


static int
xioctl (int fd, int request, void *arg)
{
    int r;


    do
r = ioctl (fd, request, arg);
    while (-1 == r && EINTR == errno);


    return r;
}


static void
process_image (const void *p)
{
    fputc ('.', stdout);
    fflush (stdout);
}


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);


 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);


 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);


 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
     errno_exit ("VIDIOC_QBUF");


 break;
      }


    return 1;
}


static void
mainloop (void)
{
    unsigned int count;


    count = 100;


    while (count-- > 0)
      {
 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 timeout/n");
     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 memory/n");
 exit (EXIT_FAILURE);
      }


    buffers[0].length = buffer_size;
    buffers[0].start = malloc (buffer_size);


    if (!buffers[0].start)
      {
 fprintf (stderr, "Out of memory/n");
 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 mapping/n", dev_name);
exit (EXIT_FAILURE);
   }
 else
   {
errno_exit ("VIDIOC_REQBUFS");
   }
      }


    if (req.count < 2)
      {
 fprintf (stderr, "Insufficient buffer memory on %s/n", dev_name);
 exit (EXIT_FAILURE);
      }


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


    if (!buffers)
      {
 fprintf (stderr, "Out of memory/n");
 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;
    unsigned int page_size;


    page_size = getpagesize ();
    buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);


    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/o/n", dev_name);
exit (EXIT_FAILURE);
   }
 else
   {
errno_exit ("VIDIOC_REQBUFS");
   }
      }


    buffers = calloc (4, sizeof (*buffers));


    if (!buffers)
      {
 fprintf (stderr, "Out of memory/n");
 exit (EXIT_FAILURE);
      }


    for (n_buffers = 0; n_buffers < 4; ++n_buffers)
      {
 buffers[n_buffers].length = buffer_size;
 buffers[n_buffers].start = memalign ( /* boundary */ page_size,
      buffer_size);


 if (!buffers[n_buffers].start)
   {
fprintf (stderr, "Out of memory/n");
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 device/n", dev_name);
exit (EXIT_FAILURE);
   }
 else
   {
errno_exit ("VIDIOC_QUERYCAP");
   }
      }


    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
      {
 fprintf (stderr, "%s is no video capture device/n", 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/o/n", 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/o/n",
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;
    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. */


    /* 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, %s/n",
  dev_name, errno, strerror (errno));
 exit (EXIT_FAILURE);
      }


    if (!S_ISCHR (st.st_mode))
      {
 fprintf (stderr, "%s is no device/n", dev_name);
 exit (EXIT_FAILURE);
      }


    fd = open (dev_name, O_RDWR /* required */  | O_NONBLOCK, 0);


    if (-1 == fd)
      {
 fprintf (stderr, "Cannot open '%s': %d, %s/n",
  dev_name, errno, strerror (errno));
 exit (EXIT_FAILURE);
      }
}


static void
usage (FILE * fp, int argc, char **argv)
{
    fprintf (fp,
    "Usage: %s [options]/n/n"
    "Options:/n"
    "-d | --device name   Video device name [/dev/video]/n"
    "-h | --help          Print this message/n"
    "-m | --mmap          Use memory mapped buffers/n"
    "-r | --read          Use read() calls/n"
    "-u | --userp         Use application allocated buffers/n"
    "", argv[0]);
}


static const char short_options[] = "d:hmru";


static const struct option long_options[] = {
    {"device", required_argument, NULL, 'd'},
    {"help", no_argument, NULL, 'h'},
    {"mmap", no_argument, NULL, 'm'},
    {"read", no_argument, NULL, 'r'},
    {"userp", no_argument, NULL, 'u'},
    {0, 0, 0, 0}
};


int
main (int argc, char *argv[])
{
    dev_name = "/dev/video0";


    for (;;)
      {
 int index;
 int c;


 c = getopt_long (argc, argv, short_options, long_options, &index);
 if (-1 == c)
   {
break;
   }
 switch (c)
   {
   case 0: /* getopt_long() flag */
break;


   case 'd':
dev_name = optarg;
break;


   case 'h':
usage (stdout, argc, argv);
exit (EXIT_SUCCESS);


   case 'm':
io = IO_METHOD_MMAP;
break;


   case 'r':
io = IO_METHOD_READ;
break;


   case 'u':
io = IO_METHOD_USERPTR;
break;


   default:
usage (stderr, argc, argv);
exit (EXIT_FAILURE);
   }
      }


    open_device ();
    return 0;
    init_device ();
    start_capturing ();
    mainloop ();
    stop_capturing ();
    uninit_device ();
    close_device ();
    exit (EXIT_SUCCESS);


    return 0;
}




### v4l2 videobuf 技术细节及使用方法 #### 1. v4l2 videobuf 的核心概念 v4l2 videobuf 是 Linux 内核中的一个重要模块,用于管理视频设备的缓冲区操作。它通过 `videobuf2` 提供了一套统一的 API 来处理不同类型的记忆体分配方式[^1]。 主要涉及的核心组件包括: - **videobuf2-core.c**: 实现了 videobuf2 的核心逻辑。 - **videobuf2-dma-contig.c 和 videobuf2-dma-sg.c**: 处理 DMA 缓冲区的相关功能。 - **videobuf2-vmalloc.c**: 支持虚拟内存分配。 - **include/uapi/linux/videobuf2.h**: 定义了与用户空间交互的数据结构和枚举类型。 其中,`enum v4l2_memory` 枚举定义了几种常见的缓冲区类型[^2]: - `V4L2_MEMORY_MMAP`: 使用内存映射的方式共享数据。 - `V4L2_MEMORY_USERPTR`: 用户指针模式,允许应用程序指定自己的缓冲区地址。 - `V4L2_MEMORY_OVERLAY`: 过时的覆盖显示模式。 - `V4L2_MEMORY_DMABUF`: 基于 DMA buffer 的跨进程资源共享机制。 #### 2. v4l2 videobuf 的典型工作流程 以下是基于 `VIDIOC_REQBUFS`, `VIDIOC_QUERYBUF`, 和其他 ioctl 调用的工作流: ##### 请求缓存 (`VIDIOC_REQBUFS`) 在实际应用中,通常会先调用 `ioctl(VIDIOC_REQBUFS)` 接口来请求一定数量的缓冲区资源。例如,在下面的例子中,程序申请了 4 个 MMAP 类型的捕获缓冲区[^4]: ```c #define NB_BUFFER 4 struct v4l2_requestbuffers reqBufs; memset(&reqBufs, 0, sizeof(struct v4l2_requestbuffers)); reqBufs.count = NB_BUFFER; reqBufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqBufs.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_REQBUFS, &reqBufs) < 0) { perror("Failed to request buffers"); close(fd); exit(EXIT_FAILURE); } ``` 如果该函数执行成功,则表示已经准备好创建所需的缓冲区数目;但此时并未真正分配物理存储器——这一步将在后续阶段完成。 ##### 查询并映射缓存 (`VIDIOC_QUERYBUF`) 接着可以利用 `VIDIOC_QUERYBUF` 获取每个单独缓冲区的具体信息,并将其映射到用户态进程中以便访问其内容: ```c for(int i=0;i<NB_BUFFER;i++) { struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if(ioctl(fd, VIDIOC_QUERYBUF, &buf)<0){ perror("Query Buffer Failed!"); break; } void* start = mmap(NULL , buf.length, PROT_READ | PROT_WRITE, MAP_SHARED , fd , buf.m.offset); if(MAP_FAILED==start){ perror("mmap failed"); continue; } // Store the pointer somewhere... } ``` 上述代码片段展示了如何逐一查询已请求好的缓冲区元数据(如长度、偏移量),并通过 POSIX 函数 `mmap()` 将它们映射至当前运行环境下的可读写区域里去。 #### 3. 设备注册与管理 当一切准备就绪后,最终还需要借助 `video_device` 结构体实例化具体的媒体实体对象,并向系统提交注册请求使得外部能够发现这些新加入的服务端点[^5]: ```c static const struct file_operations fops = {...}; struct video_device *vdev = video_device_alloc(); if (!vdev) { pr_err("Unable to allocate memory for video device\n"); return -ENOMEM; } strscpy(vdev->name, "example_v4l2", sizeof(vdev->name)); vdev->fops = &fops; vdev->release = example_release_function; vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; vdev->lock = NULL; int ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); if(ret<0){ video_device_release(vdev); return ret; } pr_info("%s registered as /dev/video%d\n",__func__,vdev->num); module_exit(example_cleanup_module); ``` 此部分负责初始化必要的属性字段值以及绑定回调句柄等内容项,从而构建起完整的驱动框架体系架构图谱。 --- ### 示范总结 综上所述,通过对以上几个关键环节的学习理解可以帮助开发者快速掌握有关于 Videobuf2 在 V4L2 子系统的具体实践技巧要点所在之处。从最基础的概念介绍再到深入剖析其实现原理直至最后给出完整可用的实际案例演示全过程均有所涉猎涵盖全面无遗漏任何重要知识点领域范围广泛适用性强值得推荐学习参考价值极高! ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值