[Bases de Linux] - Analyse du framework V4L2

I. Aperçu

Video4Linux2 est un framework de pilotes de noyau pour les périphériques vidéo dans le noyau Linux, qui fournit une interface unifiée pour la couche supérieure pour accéder aux périphériques vidéo sous-jacents. Tous les sous-systèmes du noyau résument les différences du matériel sous-jacent, fournissent une interface unifiée pour la couche supérieure et extraient des codes communs pour éviter la redondance de code et d'autres avantages. Tout comme les patrons d'une entreprise ne parlent généralement pas directement aux employés en bas, mais au chef de service pour découvrir la situation. Premièrement, il y a beaucoup de monde en bas, et les opinions sont différentes, et le libellé est inexact. Le chef de service résumera la situation par la suite. Faire un rapport à la hausse, deuxièmement, le temps du patron est précieux.

La V4L2 prend en charge trois types de périphériques: les périphériques d'entrée et de sortie vidéo, les périphériques VBI et les périphériques radio (en fait, plus de types de périphériques sont pris en charge, ce qui ne sera pas discuté pour le moment). Les nœuds de périphériques VideX, radioX et vbiX seront générés respectivement dans le répertoire / dev. Notre périphérique d'entrée vidéo commun est principalement une caméra, qui est également le principal objet d'analyse de cet article.

1.1 Le périphérique d'entrée vidéo dans le système Linux comprend principalement les quatre parties suivantes:

Le noyau du pilote de périphérique de caractères: V4L2 lui-même est un périphérique de caractères, avec toutes les caractéristiques d'un périphérique de caractères, exposant l'interface à l'espace utilisateur;

Cœur du pilote V4L2: Il s'agit principalement de créer un cadre de pilotes de périphériques vidéo standard dans le noyau et de fournir une fonction d'interface unifiée pour les opérations vidéo;

Pilote de périphérique Platform V4L2: Dans le cadre du framework V4L2, la partie pilote V4L2 était liée à la plate-forme en fonction des caractéristiques de la plate-forme elle-même, y compris l'enregistrement de video_device et v4l2_dev.

Pilote de capteur spécifique: principalement sous tension, fournir une horloge de travail, le recadrage de l'image vidéo, le démarrage du flux IO, etc., réaliser diverses méthodes de contrôle de périphérique pour que la couche supérieure appelle et enregistre v4l2_subdev.

1.2 Le code source principal de la V4L2 se trouve dans drivers / media / v4l2-core. Les fonctions implémentées par le code source peuvent être divisées en quatre catégories:

Implémentation du module de base: implémenté par v4l2-dev.c, il est principalement utilisé pour demander le numéro de périphérique principal de caractère, enregistrer la classe et fournir des fonctions liées à l'enregistrement et à l'annulation de périphérique vidéo;

Framework V4L2: implémenté par v4l2-device.c, v4l2-subdev.c, v4l2-fh.c, v4l2-ctrls.c et d'autres fichiers pour construire le framework V4L2;

Gestion Videobuf: réalisé par videobuf2-core.c, videobuf2-dma-config.c, videobuf2-dma-sg.c, videobuf2-memops.c, videobuf2-vmalloc.c, v4l2-mem2mem.c et d'autres fichiers pour compléter le Videobufffer Cession, gestion et annulation.

Framework Ioctl: Il est implémenté par le fichier v4l2-ioctl.c pour construire le framework de V4L2 ioctl.

Deux, châssis V4L2

2.1 Schéma du cadre structurel

Les structures v4l2_device, video_device, v4l2_subdev et v4l2_fh sont les principaux éléments du framework. La figure suivante est le schéma de structure du châssis V4L2:

D'après la figure ci-dessus, le framework V4L2 est une arborescence standard, v4l2_device agit comme périphérique parent et gère tous les périphériques enfants enregistrés sous celui-ci via une liste liée. Ces périphériques peuvent être GRABBER, VBI et RADIO. v4l2_subdev est un sous-périphérique. La structure v4l2_subdev contient des opérations et des ctrls qui fonctionnent sur le périphérique. Cette partie du code est liée au matériel et doit être implémentée par l'ingénieur du pilote en fonction du matériel. Les appareils photo doivent implémenter le contrôle de mise sous tension et hors tension, de lecture de l'ID, de la saturation, du contraste et de la fonction d'interface d'ouverture et de fermeture du flux de données vidéo.

Video_device est utilisé pour créer des nœuds de périphérique enfants et exposer l'interface du périphérique d'exploitation à l'espace utilisateur. v4l2_fh est le descripteur de fichier de chaque sous-périphérique. Il est défini lors de l'ouverture du fichier de nœud de périphérique pour faciliter l'index supérieur de v4l2_ctrl_handler. v4l2_ctrl_handler gère les ctrls de l'appareil. Ces ctrls (appareils photo) incluent le réglage de la saturation, du contraste et balance des blancs.

2.2 v4l2_device

v4l2_device agit en tant que périphérique parent de tous les v4l2_subdev dans le framework v4l2 et gère les périphériques enfants enregistrés sous celui-ci. Ce qui suit est le prototype de la structure v4l2_device (avec les membres non liés supprimés):

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

On peut voir que la fonction principale de v4l2_device est de gérer les sous-périphériques enregistrés sous celui-ci pour faciliter la recherche et la référence du système.

Enregistrement et annulation 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 signifie sous-périphérique et contient les attributs et opérations associés du sous-périphérique. Regardons d'abord le prototype de structure:

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

Chaque pilote de sous-périphérique doit implémenter une structure v4l2_subdev. V4l2_subdev peut être intégré dans d'autres structures ou utilisé indépendamment. La structure contient les membres v4l2_subdev_ops et v4l2_subdev_internal_ops qui fonctionnent sur des sous-appareils.

Le prototype de la structure v4l2_subdev_ops est le suivant:

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;  //视频设备的特有操作:设置帧率,裁剪图像、开关视频流等
    ... ...
};

Les périphériques vidéo doivent généralement implémenter des membres principaux et vidéo. Les opérations dans ces deux opérations sont facultatives, mais pour les périphériques de streaming vidéo, video-> s_stream (flux ouvert ou fermé IO) doit être implémentée.

Le prototype de la structure v4l2_subdev_internal_ops est le suivant:

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 est une interface fournie au framework V4L2 et ne peut être appelée que par la couche framework V4L2. Lors de l'enregistrement ou de l'ouverture d'un sous-périphérique, effectuez certaines opérations auxiliaires.

Enregistrement et désenregistrement du sous-périphérique: Lorsque nous avons implémenté tous les membres qui doivent être implémentés dans v4l2_subdev, nous pouvons appeler la fonction suivante pour enregistrer le sous-périphérique dans la couche centrale V4L2:

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

Lorsque l'appareil enfant est déchargé, la fonction suivante peut être appelée pour se déconnecter:

void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);

2.4 appareil_vidéo

La structure video_device est utilisée pour générer des fichiers de nœud de périphérique dans le répertoire / dev, exposant l'interface de fonctionnement du périphérique à l'espace utilisateur.

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;
    ... ...
};

allocation et libération de video_device, utilisées pour allouer et libérer la structure video_device:

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

Enregistrement et désinscription de Video_device. Après avoir implémenté les membres appropriés de la structure video_device, vous pouvez appeler l'interface suivante pour vous inscrire:

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 qui doit être enregistré et non enregistré;

type: type d'appareil, y compris VFL_TPYE_GRABBER, VFL_TYPE_VBI, VFL_TYPE_RADIO et VFL_TYPE_SUBDEV.

nr: numéro du nom du nœud du périphérique, tel que / dev / video [nr].

2.4 v4l2_fh

v4l2_fh est une méthode d'opération unique utilisée pour enregistrer les sous-périphériques, qui est le v4l2_ctrl_handler à analyser ci-dessous. Le noyau fournit un ensemble de méthodes d'opération v4l2_fh, généralement l'enregistrement v4l2_fh est effectué lorsque le nœud de périphérique est ouvert.

Initialisez v4l2_fh, ajoutez v4l2_ctrl_handler à v4l2_fh:

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

Ajoutez v4l2_fh à video_device pour faciliter l'appel de la couche principale:

void v4l2_fh_add(struct v4l2_fh *fh);

2.5 v4l2_ctrl_handler

v4l2_ctrl_handler est une structure utilisée pour enregistrer l'ensemble des méthodes de contrôle des sous-appareils. Pour les appareils vidéo, ces ctrls incluent le réglage de la luminosité, de la saturation, du contraste et de la netteté. Les ctrls sont enregistrés dans une liste liée. Vous pouvez ajouter des ctrls à la liste liée. via la fonction 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 est la structure v4l2_ctrl_handler initialisée;

ops est la structure v4l2_ctrl_ops, qui contient l'implémentation spécifique de ctrls;

id est la commande passée via le paramètre arg d'IOCTL, défini dans le fichier v4l2-controls.h;

min et max sont utilisés pour définir la portée d'un objet opération. Tel que:

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

L'espace utilisateur peut être appelé à v4l2_ctrl_handler via l'instruction VIDIOC_S_CTRL de ioctl, et l'id est passé via le paramètre arg.

Troisièmement, le cadre ioctl

Vous pouvez observer que les opérations de l'espace utilisateur sur les appareils V4L2 sont essentiellement réalisées par ioctl.Les appareils V4L2 ont un grand nombre de fonctions utilisables (registres de configuration), donc l'ioctl de V4L2 est également très grand. De quel type de cadre s'agit-il et comment est-il mis en œuvre?

Le framework Ioctl est implémenté par le fichier v4l2_ioctl.c. La structure définie dans le fichier est le tableau v4l2_ioctls, qui peut être considéré comme la table des relations entre la commande ioctl et la fonction de rappel. L'espace utilisateur appelle l'appel système ioctl, transmet l'instruction ioctl, puis trouve la fonction de rappel correspondante en recherchant la table des relations.

Voici deux éléments du tableau intercepté:

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

Le noyau fournit deux macros (IOCTL_INFO_FNC et IOCTL_INFO_STD) pour initialiser la structure. Les paramètres sont l'instruction ioctl, la fonction de rappel ou le membre de la structure v4l2_ioctl_ops, la fonction de débogage et l'indicateur. Si la fonction de rappel est membre de la structure v4l2_ioctl_ops, utilisez IOCTL_INFO_STD; si la fonction de rappel est implémentée par v4l2_ioctl.c, utilisez IOCTL_INFO_FNC.

L'organigramme de l'appel IOCTL est le suivant:

L'espace utilisateur obtient la structure de fichier du fichier en ouvrant le nœud de périphérique sous le répertoire / dev /, et transmet cmd et arg au noyau via l'appel système ioctl. Après une série d'appels, la fonction _video_do_ioctl est finalement appelée, puis v4l2_ioctls [] est récupérée via cmd pour déterminer s'il s'agit d'INFO_FL_STD ou d'INFO_FL_FUNC. S'il s'agit de INFO_FL_STD, il appellera directement la fonction video_device-> v4l2_ioctl_ops définie dans le pilote de périphérique vidéo. S'il s'agit de INFO_FL_FUNC, il appellera d'abord la fonction de rappel standard implémentée par v4l2, puis appelera le jeu de fonctions video_device-> v4l2_ioctl_ops ou v4l2_fh-> v4l2_ctrl_handler en fonction de l'argument.

Expansion:

1. Cadre de contrôle v4l2: Étant donné que l'entrée vidéo est impliquée, il y aura de nombreux effets liés au FAI, tels que le contraste, la saturation, la température de couleur, la balance des blancs, etc., ce sont des éléments de contrôle courants et nécessaires, et la plupart d'entre eux seulement Vous besoin de définir une valeur entière. V4L2 très proche de nous fournir un tel nombre d'interfaces à utiliser (on peut dire que c'est très intime), à ​​l'intérieur du noyau, ces contrôles sont abstraits comme l'un des ID de contrôle, respectivement V4L2_CID_XXXnommé.

2. Framework ioctl v4l2: les opérations de l'espace utilisateur sur les appareils V4L2 sont essentiellement réalisées via un ioctl incorrect, tel que VIDIOC_QUERYCAP, VIDIOC_S_FMT, VIDIOC_DQBUF, etc.

Quatre, accès IO

La V4L2 prend en charge trois méthodes d'accès IO différentes (d'autres méthodes d'accès sont également prises en charge dans le noyau et ne seront pas abordées pour le moment):

  1. La lecture et l'écriture sont des méthodes basiques d'accès aux E / S de trame. Chaque trame de données est lue par lecture. Les données doivent être copiées entre le noyau et l'utilisateur. Cette méthode d'accès peut être très lente;
  2. Le tampon mappé en mémoire (V4L2_MEMORY_MMAP), qui ouvre en fait le tampon dans l'espace noyau, est mappé à l'espace d'adressage utilisateur par l'application via l'appel système mmap (). Ces tampons peuvent être des tampons DMA volumineux et continus, des tampons virtuels créés par vmalloc () ou des tampons ouverts directement dans la mémoire IO du périphérique (si pris en charge par le matériel);
  3. Le tampon de l'espace utilisateur (V4L2_MEMORY_USERPTR) permet à l'application de l'espace utilisateur d'ouvrir un tampon, et d'échanger des pointeurs de tampon entre l'utilisateur et l'espace noyau. Évidemment, mmap () n'est pas nécessaire dans ce cas, mais le pilote supportera efficacement les tampons d'espace utilisateur, et son travail sera plus difficile.

Les méthodes de lecture et d'écriture appartiennent à la méthode d'accès aux E / S de trame. Chaque trame doit être exploitée via IO, ce qui nécessite une copie des données entre l'utilisateur et le noyau. Les deux dernières sont des méthodes d'accès aux E / S de flux, qui ne nécessitent pas de copie de mémoire, et la la vitesse d'accès est relativement rapide. La méthode d'accès à la mémoire tampon mappée en mémoire est une méthode plus couramment utilisée.

4.1 Mode tampon mappé en mémoire

Flux de données au niveau de la couche matérielle

Les données d'image capturées par le capteur de la caméra sont transmises à CAMIF (interface de caméra) via le port parallèle ou MIPI, et CAMIF peut ajuster les données d'image (retournement, recadrage, conversion de format, etc.). Ensuite, le contrôleur DMA règle le canal DMA pour demander à AHB de transférer les données d'image vers le tampon DMA alloué.

Une fois les données d'image transférées vers la mémoire tampon DMA, l'opération mmap mappe la mémoire tampon sur l'espace utilisateur et l'application peut accéder directement aux données dans la mémoire tampon.

4.2 vb2_queue

Pour que le périphérique prenne en charge le flux IO, le pilote doit implémenter struct vb2_queue, regardez cette structure:

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 représente une file d'attente de tampon vidéo, vb2_buffer est membre de cette file d'attente, vb2_mem_ops est l'ensemble de fonctions d'opération de la mémoire tampon et vb2_ops est utilisé pour gérer la file d'attente.

4.3 vb2_mem_ops

vb2_mem_ops contient les méthodes de fonctionnement de la mémoire du tampon mappé en mémoire et du tampon de l'espace utilisateur:

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);    //把缓冲区映射到用户空间
};

C'est une structure très volumineuse, tant de structures doivent être implémentées. Heureusement, l'un des noyaux nous a aidés à la réaliser. Trois types d'opérations de mise en cache vidéo sont fournis:

  1. Tampon DMA continu;
  2. Tampon DMA distribué;
  3. Buffer créé par vmalloc.

Ils sont implémentés respectivement par les fichiers videobuf2-dma-contig.c, videobuf2-dma-sg.c et videobuf-vmalloc.c, et peuvent être utilisés en fonction de la situation réelle.

4.4 vb2_ops

vb2_ops est un ensemble de fonctions utilisées pour gérer la file d'attente du tampon, y compris l'initialisation de la file d'attente et du tampon

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 est l'unité de base de la file d'attente de tampon, et v4l2_buffer incorporé dans celle-ci est le membre principal. Lorsque le streaming IO est lancé, la trame est transmise entre l'application et le pilote au format v4l2_buffer. Un tampon peut avoir trois états:

  1. Dans la file d'attente entrante du pilote, le pilote traitera le tampon de cette file d'attente et l'espace utilisateur placera le tampon dans la file d'attente via IOCTL: VIDIOC_QBUF. Pour un périphérique de capture vidéo, la mémoire tampon de la file d'attente entrante est vide et le pilote la remplira de données;

  2. Dans la file d'attente sortante du pilote , ces tampons ont été traités par le pilote.Pour un périphérique de capture vidéo, le tampon a été rempli de données vidéo et attend que l'espace utilisateur les revendique;

  3. La file d'attente de l'état de l'espace utilisateur a été transmise à la mémoire tampon de l'espace utilisateur via IOCTL: VIDIOC_DQBUF. À ce stade, la mémoire tampon appartient à l'espace utilisateur et n'est pas accessible par le pilote.

La commutation de ces trois états est illustrée dans la figure ci-dessous:

La structure de v4l2_buffer est la suivante:

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

Lorsque l'espace utilisateur obtient v4l2_buffer, les informations pertinentes du tampon peuvent être obtenues. byteused est le nombre d'octets occupés par les données d'image:

  • S'il s'agit de la méthode V4L2_MEMORY_MMAP, m.offset est l'adresse de départ du stockage des données d'image dans l'espace noyau, qui sera transmise à la fonction mmap en tant que décalage, et un pointeur de tampon p est renvoyé via le mappage mmap, p + byteused est le espace d'adressage virtuel des données d'image dans le processus Zone occupée
  • S'il s'agit d'un mode tampon de pointeur utilisateur, le pointeur d'adresse de début des données d'image pouvant être obtenues est m.userptr, userptr est un pointeur d'espace utilisateur, userptr + byteused est l'espace d'adressage virtuel occupé, et l'application peut y accéder directement .

Cinq, équipement d'accès à l'espace utilisateur

Ce qui suit est le processus d'accès au périphérique vidéo (périphérique de capture) via le tampon de mappage du noyau.

1> Ouvrez le fichier de l'appareil

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

2> Interrogez les capacités prises en charge par l'appareil

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

3> Définir le format de capture vidéo

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> Demander un tampon au pilote

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> Obtenez les informations de chaque tampon et mappez-les à l'espace utilisateur

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> Mettez le tampon dans la file d'attente entrante, ouvrez le flux IO et démarrez la capture vidéo

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> Appelez sélectionnez pour surveiller le descripteur de fichier, si les données dans la mémoire tampon sont remplies, puis vérifiez les données vidéo

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> Retirez le tampon rempli, obtenez la taille des données vidéo, puis traitez les données. Le tampon extrait ici ne contient que les informations du tampon et les données vidéo ne sont pas copiées.

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> Arrêter la capture vidéo

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

10> Éteignez l'appareil

close(fd);

Attaché:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Je suppose que tu aimes

Origine blog.csdn.net/u014674293/article/details/113701713
conseillé
Classement