[Conceptos básicos de Linux] - Análisis del marco V4L2

I. Panorama general

Video4Linux2 es un marco de controlador de kernel para dispositivos de video en el kernel de Linux, que proporciona una interfaz unificada para que la capa superior acceda a los dispositivos de video subyacentes. Todos los subsistemas del kernel abstraen las diferencias del hardware subyacente, proporcionan una interfaz unificada para la capa superior y extraen códigos comunes para evitar la redundancia de código y otros beneficios. Al igual que los jefes de una empresa generalmente no hablan directamente con los empleados en la base, sino con el gerente del departamento para averiguar la situación. Una es porque hay muchas personas en la base y las opiniones son diferentes, y la redacción es inexacto. El gerente del departamento resumirá la situación después. Informe hacia arriba; en segundo lugar, el tiempo del jefe es precioso.

V4L2 admite tres tipos de dispositivos: dispositivos de entrada y salida de video, dispositivos VBI y dispositivos de radio (de hecho, se admiten más tipos de dispositivos, que no se discutirán por el momento). Los nodos de dispositivos VideX, radioX y vbiX serán generado en el directorio / dev respectivamente. Nuestro dispositivo de entrada de video común es principalmente una cámara, que también es el principal objeto de análisis de este artículo.

1.1 El dispositivo de entrada de video en el sistema Linux incluye principalmente las siguientes cuatro partes:

El núcleo del controlador de dispositivo de caracteres: V4L2 en sí mismo es un dispositivo de caracteres, con todas las características de un dispositivo de caracteres, exponiendo la interfaz al espacio del usuario;

Núcleo del controlador V4L2: es principalmente para construir un marco de controladores de dispositivos de video estándar en el kernel y proporcionar una función de interfaz unificada para operaciones de video;

Controlador de dispositivo de plataforma V4L2: En el marco de V4L2, la parte del controlador de V4L2 se relaciona con la plataforma de acuerdo con las características de la propia plataforma, incluido el registro de video_device y v4l2_dev.

Controlador de sensor específico: principalmente, encienda, proporcione reloj de trabajo, recorte de imágenes de video, inicio de E / S de transmisión, etc., realice varios métodos de control de dispositivos para que la capa superior llame y registre v4l2_subdev.

1.2 El código fuente central de V4L2 se encuentra en drivers / media / v4l2-core. Las funciones implementadas por el código fuente se pueden dividir en cuatro categorías:

Implementación del módulo principal: implementado por v4l2-dev.c, se utiliza principalmente para solicitar el número de dispositivo principal de caracteres, registrar la clase y proporcionar funciones relacionadas con el registro y cancelación de dispositivos de video;

Marco V4L2: implementado por v4l2-device.c, v4l2-subdev.c, v4l2-fh.c, v4l2-ctrls.c y otros archivos para construir el marco V4L2;

Gestión de videobuf: realizada por videobuf2-core.c, videobuf2-dma-config.c, videobuf2-dma-sg.c, videobuf2-memops.c, videobuf2-vmalloc.c, v4l2-mem2mem.c y otros archivos para completar el Asignación, gestión y cancelación de videobufffer.

Marco de Ioctl: se implementa mediante el archivo v4l2-ioctl.c para construir el marco de trabajo de V4L2 ioctl.

Dos, marco V4L2

2.1 Diagrama de estructura estructural

Las estructuras v4l2_device, video_device, v4l2_subdev y v4l2_fh son los elementos principales del marco. La siguiente figura es el diagrama de estructura del marco V4L2:

De la figura anterior, el marco V4L2 es una estructura de árbol estándar, v4l2_device actúa como el dispositivo principal y administra todos los dispositivos secundarios registrados en él a través de una lista vinculada Estos dispositivos pueden ser GRABBER, VBI y RADIO. v4l2_subdev es un subdispositivo. La estructura v4l2_subdev contiene ops y ctrls que operan en el dispositivo. Esta parte del código está relacionada con el hardware y debe ser implementada por el ingeniero del controlador de acuerdo con el hardware. Los dispositivos de la cámara deben implementar el control de encendido y apagado, ID de lectura, saturación, contraste y función de interfaz de abrir y cerrar el flujo de datos de video.

Video_device se utiliza para crear nodos de dispositivos secundarios y exponer la interfaz del dispositivo operativo al espacio del usuario. v4l2_fh es el identificador de archivo de cada subdispositivo. Se establece al abrir el archivo de nodo del dispositivo para facilitar el índice superior de v4l2_ctrl_handler. v4l2_ctrl_handler administra los controles del dispositivo. Estos controles (dispositivos de cámara) incluyen el ajuste de saturación, contraste y balance de blancos.

2.2 v4l2_device

v4l2_device actúa como el dispositivo principal de todos los v4l2_subdev en el marco v4l2 y administra los dispositivos secundarios registrados en él. El siguiente es el prototipo de la estructura v4l2_device (con miembros no relacionados eliminados):

struct v4l2_device {
    struct list_head subdevs; //用链表管理注册的 subdev
    char name[V4L2_DEVICE_NAME_SIZE]; //device 名字
    struct kref ref;    //引用计数
    ... ...
};

Se puede ver que la función principal de v4l2_device es administrar los subdispositivos registrados en él para facilitar la búsqueda y referencia del sistema.

Registro y cancelación de v4l2_device:

int v4l2_device_register(struct device *dev, struct v4l2_device *V4l2_dev);
static void v4l2_device_release(struct kref *ref);

2.3 V4l2_subdev

v4l2_subdev significa subdispositivo y contiene los atributos y operaciones relacionados del subdispositivo. Veamos primero el prototipo de estructura:

struct v4l2_subdev {
    struct v4l2_device *v4l2_dev;    //指向父设备
    const struct v4l2_subdev_ops *ops; //提供一些控制 v4l2 设备的接口
    const struct v4l2_subdev_internal_ops; *internal_ops; //向 v4l2 框架提供的接口函数
    
    //subdev 控制接口
    struct v4l2_ctrl_handler *ctrl_handler;
    /* name nust be unique */
    char name[V4L2_SUBDEV_NAME_SIZE];
    
    /* subdev device node */
    struct video_device *devnode;
    
};

Cada controlador de subdispositivo debe implementar una estructura v4l2_subdev. V4l2_subdev puede integrarse en otras estructuras o usarse de forma independiente. La estructura contiene miembros v4l2_subdev_ops y v4l2_subdev_internal_ops que operan en subdispositivos.

El prototipo de la estructura v4l2_subdev_ops es el siguiente:

struct v4l2_subdev_ops {
    const struct v4l2_subdev_core_ops *core;    //视频设备通用的操作:初始化、加载FW、上电和 RESET 等
    const struct v4l2_subdev_tuner_ops *tuner;  //tuner 特有的操作
    const struct v4l2_subdev_audio_ops *audio;  //audio 特有的操作
    const struct v4l2_subdev_video_ops *video;  //视频设备的特有操作:设置帧率,裁剪图像、开关视频流等
    ... ...
};

Los dispositivos de video generalmente necesitan implementar miembros centrales y de video. Las operaciones en estas dos operaciones son opcionales, pero para los dispositivos de transmisión de video video-> s_stream (open o close stream IO) debe implementarse.

El prototipo de la estructura v4l2_subdev_internal_ops es el siguiente:

struct v4l2_subdev_internal_ops {
    //当 subdev 注册时被调用,读取 IC 的 ID 来进行识别
    int (*registered)(struct v4l2_subdev *sd);
    void (*unsigned)(struct v4l2_subdev *sd);
    //当设备节点被打开时调用,通常会给设备上电和设置视频捕捉 FMT
    int (*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
    int (*close)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
};

v4l2_subdev_internal_ops es una interfaz proporcionada al marco V4L2 y solo puede ser invocada por la capa del marco V4L2. Al registrar o abrir un subdispositivo, realice algunas operaciones auxiliares.

Registro y desregistro de subdev: Cuando hemos implementado todos los miembros que deben implementarse en v4l2_subdev, podemos llamar a la siguiente función para registrar el subdispositivo en la capa central de V4L2:

int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd);

Cuando se descarga el dispositivo secundario, se puede llamar a la siguiente función para cerrar la sesión:

void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);

2.4 video_device

La estructura video_device se usa para generar archivos de nodo de dispositivo en el directorio / dev, exponiendo la interfaz de operación del dispositivo al espacio del usuario.

struct video_device {
    const struct v4l2_file_operations *fops;
    
    /* sysfs */
    struct device dev;    /* v4l device */
    struct cdev *cdev;    // 字符设备

    /* Seteither parent or v4l2_dev if your driver uses v4l2_device */
    struct device *parent;    /* device parent */
    struct v4l2_device *v4l2_dev;    /* v4l2_device parent */
    
    /* Control handler associated with this device node. May be NULL */
    struct v4l2_ctrl_handler *ctrl_handler;

    /* 指向 video buffer 队列 */
    struct vb2_queue *queue;

    int vfl_type;    /* device type */
    int minor;       //次设备号
    
    /* V4L2 file handles */
    spin lock_t fh_lock;    /* lock for all v4l2_fhs */
    struct list_head fh_list    /* List of struct v4l2_fh */
    
    /* ioctl 回调函数集,提供 file_operations 中的 ioctl 调用 */    
    const struct v4l2_ioctl_ops *ioctl_ops;
    ... ...
};

Asignación y liberación de video_device, que se utiliza para asignar y liberar la estructura video_device:

struct video_device *video_device_alloc(void);
void video_device_release(struct video_device *vdev);

Registro y cancelación del registro de video_device. Después de implementar los miembros relevantes de la estructura video_device, puede llamar a la siguiente interfaz para registrarse:

static inline int __must_checkvideo_register_device(struct video_device *vdev, int type, int nr);
void void_unregister_device(struct video_device *vdev);

vdev: video_device que debe registrarse y desregistrarse;

type: tipo de dispositivo, incluidos VFL_TPYE_GRABBER, VFL_TYPE_VBI, VFL_TYPE_RADIO y VFL_TYPE_SUBDEV.

nr: número de nombre de nodo del dispositivo, como / dev / video [nr].

2.4 v4l2_fh

v4l2_fh es un método de operación único utilizado para guardar subdispositivos, que es el v4l2_ctrl_handler que se analizará a continuación. El kernel proporciona un conjunto de métodos de operación v4l2_fh, generalmente el registro v4l2_fh se realiza cuando se abre el nodo del dispositivo.

Inicialice v4l2_fh, agregue v4l2_ctrl_handler a v4l2_fh:

void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev);

Agregue v4l2_fh a video_device para facilitar la capa central para llamar:

void v4l2_fh_add(struct v4l2_fh *fh);

2.5 v4l2_ctrl_handler

v4l2_ctrl_handler es una estructura que se usa para guardar el conjunto de métodos de control de subdispositivos. Para los dispositivos de video, estos controles incluyen la configuración de brillo, saturación, contraste y nitidez. Los controles se guardan en una lista vinculada. Puede agregar controles a la lista vinculada a través de la función v4l2_ctrl_new_std.

struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s32 min, s32 max, u32 step, s32 def);

hdl es la estructura v4l2_ctrl_handler inicializada;

ops es la estructura v4l2_ctrl_ops, que contiene la implementación específica de ctrls;

id es el comando pasado a través del parámetro arg de IOCTL, definido en el archivo v4l2-controls.h;

min y max se utilizan para definir el alcance de un objeto de operación. Tal como:

v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -208, 127, 1, 0);

El espacio de usuario se puede llamar a v4l2_ctrl_handler a través de la instrucción VIDIOC_S_CTRL de ioctl, y el id se pasa a través del parámetro arg.

Tres, marco ioctl

Puede observar que las operaciones del espacio de usuario en los dispositivos V4L2 las realiza básicamente ioctl. Los dispositivos V4L2 tienen una gran cantidad de funciones operables (registros de configuración), por lo que el ioctl de V4L2 también es muy grande. ¿Qué tipo de marco es y cómo se implementa?

El marco Ioctl se implementa mediante el archivo v4l2_ioctl.c. La estructura definida en el archivo es la matriz v4l2_ioctls, que puede considerarse como la tabla de relaciones entre el comando ioctl y la función de devolución de llamada. El espacio de usuario llama a la llamada al sistema ioctl, transmite la instrucción ioctl y luego encuentra la función de devolución de llamada correspondiente buscando la tabla de relaciones.

Los siguientes son dos elementos de la matriz interceptada:

IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l2_querybuf, v4l_printf_buffer, INFO_FL_CLEAR(v4l2_buffer, length)),
IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),

El kernel proporciona dos macros (IOCTL_INFO_FNC e IOCTL_INFO_STD) para inicializar la estructura.Los parámetros son instrucción ioctl, función de devolución de llamada o miembro de estructura v4l2_ioctl_ops, función de depuración y marca. Si la función de devolución de llamada es un miembro de la estructura v4l2_ioctl_ops, use IOCTL_INFO_STD; si la función de devolución de llamada es implementada por v4l2_ioctl.c, use IOCTL_INFO_FNC.

El diagrama de flujo de la convocatoria IOCTL es el siguiente:

El espacio de usuario obtiene la estructura de archivo del archivo abriendo el nodo del dispositivo en el directorio / dev / y pasa cmd y arg al kernel a través de la llamada al sistema ioctl. Después de una serie de llamadas, finalmente se llama a la función _video_do_ioctl y luego se recupera v4l2_ioctls [] a través de cmd para determinar si es INFO_FL_STD o INFO_FL_FUNC. Si es INFO_FL_STD, llamará directamente a la función video_device-> v4l2_ioctl_ops establecida en el controlador del dispositivo de video. Si es INFO_FL_FUNC, primero llamará a la función de devolución de llamada estándar implementada por v4l2, y luego llamará a la función video_device-> v4l2_ioctl_ops o v4l2_fh-> v4l2_ctrl_handler establecida de acuerdo con el argumento arg.

Expansión:

1. Marco de control v4l2: dado que la entrada de video está involucrada, habrá muchos efectos relacionados con el ISP, como contraste, saturación, temperatura de color, balance de blancos, etc., estos son elementos de control comunes y necesarios, y la mayoría de ellos solo usted necesita establecer un valor entero. V4L2 muy cerca de proporcionarnos tal cantidad de interfaces para que las usemos (se puede decir que es muy íntimo), dentro del kernel, estos controles se abstraen como uno de los ID de control, respectivamente V4L2_CID_XXXnombrados.

2. Marco ioctl v4l2: las operaciones de espacio de usuario en dispositivos V4L2 se realizan básicamente a través de ioctl incorrectos, como VIDIOC_QUERYCAP, VIDIOC_S_FMT, VIDIOC_DQBUF, etc.

Cuatro, acceso IO

V4L2 admite tres métodos de acceso de E / S diferentes (otros métodos de acceso también son compatibles con el kernel y no se discutirán por el momento):

  1. La lectura y la escritura son métodos básicos de acceso de E / S de tramas. Cada trama de datos se lee a través de lectura. Los datos deben copiarse entre el núcleo y el usuario. Este método de acceso puede ser muy lento;
  2. El búfer mapeado en memoria (V4L2_MEMORY_MMAP), que en realidad abre el búfer en el espacio del kernel, se asigna al espacio de direcciones del usuario por la aplicación a través de la llamada al sistema mmap (). Estos búferes pueden ser búferes DMA grandes y continuos, búferes virtuales creados por vmalloc () o búferes abiertos directamente en la memoria de E / S del dispositivo (si es compatible con el hardware);
  3. El búfer de espacio de usuario (V4L2_MEMORY_USERPTR) permite a la aplicación de espacio de usuario abrir un búfer e intercambiar punteros de búfer entre el usuario y el espacio del kernel. Obviamente, mmap () no es necesario en este caso, pero el controlador admitirá de manera efectiva los búferes de espacio de usuario y su trabajo será más difícil.

Los métodos de lectura y escritura pertenecen al método de acceso de E / S de trama. Cada trama debe operarse a través de E / S, que requiere copia de datos entre el usuario y el núcleo. Los dos últimos son métodos de acceso de E / S de flujo, que no requieren copia de memoria, la velocidad de acceso es relativamente rápida. El método de acceso al búfer mapeado en memoria es un método más utilizado.

4.1 Modo de búfer mapeado en memoria

Transmisión de datos en la capa de hardware

Los datos de la imagen capturados por el sensor de la cámara se transmiten a CAMIF (interfaz de la cámara) a través del puerto paralelo o MIPI, y CAMIF puede ajustar los datos de la imagen (voltear, recortar, conversión de formato, etc.). Luego, el controlador DMA establece el canal DMA para solicitar a AHB que transfiera los datos de imagen al búfer DMA asignado.

Una vez que los datos de la imagen se transfieren al búfer DMA, la operación mmap asigna el búfer al espacio del usuario y la aplicación puede acceder directamente a los datos en el búfer.

4.2 vb2_queue

Para que el dispositivo admita E / S de flujo, el controlador necesita implementar struct vb2_queue, observe esta estructura:

struct vb2_queue {
    enum v4l2_buf_type type;    //buffer 类型
    unsigned int io_modes;    //访问 IO 的方式:mmap、userptr etc
    const struct vb2_ops *ops;    //buffer 队列操作函数集合
    const struct vb2_mem_ops *mem_ops;    //buffer memory 操作集合
    struct vb2_buffer *bufs[VIDEO_MAX_FRAME];    //代表每个 buffer
    unsigned int num_buffers;    //分配的 buffer 个数
    ... ...
};

vb2_queue representa una cola de búfer de video, vb2_buffer es un miembro de esta cola, vb2_mem_ops es el conjunto de funciones de operación de la memoria búfer y vb2_ops se usa para administrar la cola.

4.3 vb2_mem_ops

vb2_mem_ops contiene los métodos de operación de memoria del búfer asignado a la memoria y del búfer de espacio de usuario:

struct vb2_mem_ops {
    void *(*alloc)(void *alloc_ctx, unsigned long size); //分配视频缓存
    void (*put)(void *buf_priv);    //释放视频缓存
    
    //获取用户空间视频缓冲区指针
    void *(*get_userptr)(void **alloc_ctx, unsigned long vaddr, unsigned long size, int write);
    void (*put_userptr)(void *buf_priv);    //释放用户空间视频缓冲区指针
    
    //用于缓冲同步
    void (*prepare)(void *buf_priv);
    void (*finish)(void *buf_priv);
    void *(*vaddr)(void *buf_priv);
    void *(*cookie)(void *buf_priv);
    unsigned int (*num_users)(void *buf_priv);    //返回当期在用户空间的buffer数
    int (*mmap)(void *buf_priv, struct vm_area_struct *vma);    //把缓冲区映射到用户空间
};

Esta es una estructura muy grande, por lo que hay que implementar muchas estructuras. Afortunadamente, uno de los núcleos nos ayudó a darnos cuenta. Se proporcionan tres tipos de operaciones de almacenamiento en caché de video:

  1. Búfer DMA continuo;
  2. Búfer DMA distribuido;
  3. Búfer creado por vmalloc.

Se implementan mediante los archivos videobuf2-dma-contig.c, videobuf2-dma-sg.c y videobuf-vmalloc.c respectivamente, y se pueden utilizar de acuerdo con la situación real.

4.4 vb2_ops

vb2_ops es una colección de funciones que se utilizan para administrar la cola del búfer, incluida la inicialización de la cola y el búfer

struct vb2_ops {
    //队列初始化
    int (*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]);
    
    //释放和获取设备操作锁
    void (*wait_prepare)(struct vb2_queue *q);
    void (*wait_finish)(struct vb2_queue *q);
    
    //对 buffer 的操作
    int (*buf_init)(struct vb2_buffer *vb);
    int (*buf_prepare)(struct vb2_buffer *vb);
    int (*buf_finish)(struct vb2_buffer *vb);
    int (*buf_cleanup)(struct vb2_buffer *vb);
    
    //开始视频流
    int (*start_streaming)(struct vb2_queue *q, unsigned int count);
      
    //停止视频流
    int (*stop_streaming)(struct vb2_queue *q);
    
    //把 VB 传递给驱动
    void (*buf_queue)(struct vb2_buffer *vb);
};

vb2_buffer es la unidad básica de la cola de búfer y v4l2_buffer incrustado en ella es el miembro principal. Cuando se inicia la transmisión de E / S, la trama se transmite entre la aplicación y el controlador en el formato v4l2_buffer. Un búfer puede tener tres estados:

  1. En la cola de entrada del controlador, el controlador procesará el búfer en esta cola y el espacio de usuario colocará el búfer en la cola a través de IOCTL: VIDIOC_QBUF. Para un dispositivo de captura de video, el búfer de la cola entrante está vacío y el controlador lo llenará con datos;

  2. En la cola de salida del controlador , estos búferes han sido procesados ​​por el controlador Para un dispositivo de captura de video, el búfer se ha llenado con datos de video y está esperando que el espacio del usuario lo reclame;

  3. La cola del estado del espacio del usuario se ha transmitido al búfer del espacio del usuario a través de IOCTL: VIDIOC_DQBUF. En este momento, el búfer es propiedad del espacio del usuario y el controlador no puede acceder a él.

La conmutación de estos tres estados se muestra en la siguiente figura:

La estructura de v4l2_buffer es la siguiente:

struct v4l2_buffer {
    __u32 index;    //buffer 序号
    __u32 type;     //buffer 类型
    __u32 byteused; //缓冲区已使用 byte 数
    __u32 flags;    
    __u32 field;
    struct timeval timestamp;    //时间戳,代表帧捕获的时间
    struct v4l2_timecode timecode;
    __u32 sequence;
    
    /* memory location */
    __u32 memory;    //代表缓冲区是内存映射缓冲区还是用户空间缓冲区
    
    union {
        __u32 offset;    //内核缓冲区的位置
        unsigned long userptr;    //缓冲区的用户空间地址
        struct v4l2_plane *planes;
        __s32 fd;
    }m;
    __u32 length;    //缓冲区大小,单位 byte
};

Cuando el espacio de usuario obtiene v4l2_buffer, se puede obtener la información relevante del búfer. byteused es el número de bytes ocupados por datos de imagen:

  • Si es el método V4L2_MEMORY_MMAP, m.offset es la dirección de inicio del almacenamiento de datos de imagen en el espacio del kernel, que se pasará a la función mmap como un desplazamiento, y se devuelve un puntero de búfer p a través del mapeo de mmap, p + byteused es el espacio de direcciones virtual de los datos de la imagen en el proceso Área ocupada
  • Si es un modo de búfer de puntero de usuario, el puntero de dirección de inicio de los datos de imagen que se pueden obtener es m.userptr, userptr es un puntero de espacio de usuario, userptr + byteused es el espacio de direcciones virtuales ocupado y la aplicación puede acceder directamente a él .

Cinco, equipo de acceso al espacio de usuario

El siguiente es el proceso de acceso al dispositivo de video (dispositivo de captura) a través del búfer de mapeo del kernel.

1> Abra el archivo del dispositivo

fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
//dev_name[ /dev/videoX ]

2> Consultar las capacidades admitidas por el dispositivo

struct v4l2_capability cap;
ioctl(fd, VIDIOC_QUERYCAP, &cap);

3> Establecer formato de captura de video

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;

ioctl(fd, VIDIOC_S_FMT, &fmt);

4> Solicitar búfer al conductor

struct v4l2_buffer req;
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;

if(-1 == xioctl(fd, VIDIOC_REQBUFS, &req))

5> Obtener la información de cada búfer y asignarla al espacio del usuario

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

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

for(n_buffers = 0; n_buffers < req.count; ++n_buffers){
    
    struct v4l2_buffer 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);
}

6> Ponga el búfer en la cola entrante, abra el flujo IO y comience la captura de video

for(i=0; i<n_buffers; ++i){
    
    struct v4l2_buffer 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))
}

7> Seleccionar llamada para monitorear el descriptor de archivo, si los datos en el búfer están llenos, y luego verifique los datos de video

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\r\n");
        exit(EXIT_FAILURE);
    }
    
    //对视频数据进行处理
    if(read_frame())
        break;
    
    /* EAGAIN - continue select loop. */
}

8> Saque el búfer lleno, obtenga el tamaño de los datos de video y luego procese los datos. El búfer extraído aquí solo contiene la información del búfer y los datos de vídeo no se copian.

buf.type = V4L2_BUF_TPYE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;

if(-1 == ioctl(fd, VIDIOC_DQBUF, &buf))    //取出缓存
    errno_exit("VIDIOC_QBUF");

process_image(buffers[buf.index].start, buf.byteused);    //视频数据处理

if(-1 == xioctl(fd, VIDIOC_QBUF, &buf))    //然后又放入到传入队列
    errno_exit("VIDIOC_QBUF");

9> Detener la captura de video

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMOFF, &type);

10> Apaga el dispositivo

close(fd);

Adjunto:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Supongo que te gusta

Origin blog.csdn.net/u014674293/article/details/113701713
Recomendado
Clasificación