Capture de vidéo à partir de caméras USB sur des appareils Linux

Contexte


Il y a quelque temps, j'ai été tenté «d'améliorer» un char de l'ensemble bien connu «Tank battle», en ajoutant la possibilité de jouer comme si j'étais un pilote de char. L'idée est apparue après avoir lu plusieurs articles sur Habré (par exemple, ici: geektimes.ru/post/257528), j'ai trouvé en eux comment cela peut être fait avec un petit routeur WiFi et une caméra USB. La solution était d'une simplicité captivante: le routeur est flashé avec un micrologiciel spécial, la caméra y est connectée, le réservoir est contrôlé par la télécommande native et la vidéo apparaît dans le navigateur. Ayant rapidement assemblé le prototype, j'ai trouvé que la vidéo était capturée d'une qualité dégoûtante. C'était 320x440x30 ou 640x480x30. Lorsque le mode 1280x720 a été activé, au mieux il y avait une vidéo déchirée avec des artefacts, au pire ce n'était pas du tout. Le mode 1920x1080 ne fonctionnait pas en principe. Cela m'a beaucoup bouleversé, car sur un PC, la caméra prend en charge les modes jusqu'à 1920x1080x30 et a une compression matérielle MJPG. Mon intuition suggère que la mise en œuvre est loin d'être parfaite.

Buts


  1. Vidéo en résolution FullHD (1920X1080) ou HD (1280x720) et fréquence d'images normale (pour que vous puissiez jouer).
  2. J'avais prévu de donner le jouet aux enfants, j'avais donc besoin d'un démarrage automatique et d'un support pour connecter / déconnecter la caméra.

En général, je voulais quelque chose comme ça:



Limites


Je n'allais pas chercher une solution qui fonctionne toujours et partout. Les restrictions suivantes me convenaient parfaitement:

  1. Bon signal wifi.
  2. Un nombre limité de connexions, la priorité a été donnée au cas où il n'y a qu'un seul client.
  3. La caméra prend en charge le mode MJPG.

HW et SW


  1. Caméscope Logitech B910 HD ( http://www.logitech.com/en-us/product/b910-hd-webcam ).
  2. Routeur TP-LINK TL-MR3020. Ce bébé possède le matériel suivant: CPU MIPS 24K 400 MHz, RAM 32 Mio, Flash 4 Mio, Ethernet 100 Mbit, USB 2.0 ( http://wiki.openwrt.org/en/toh/tp-link/tl-mr3020 ).
  3. . OR-WRT (http://roboforum.ru/wiki/OR-WRT), OpenWRT (http://openwrt.org/, 12.07 15.05).
  4. . , .
  5. “ ”.


En général, c'est une configuration vraiment faible, surtout si vous vous souvenez qu'un cadre au format YUV420 de taille 1920X1080 prend 4 Mo (2 octets par pixel). J'ai été encouragé par le fait que la caméra prend en charge la compression matérielle MJPG. Des expériences ont montré qu'une trame FullHD compressée est généralement <500 Ko. J'ai donc décidé de poursuivre la recherche. Il s'est avéré que mjpg-streamer (http://sourceforge.net/projects/mjpg-streamer/) est utilisé pour capturer la vidéo et la diffuser via HTTP. L'analyse de son code a montré qu'il utilise 1 flux pour capturer la vidéo + un flux séparé pour chaque client. Ce n'est pas la meilleure solution pour un système monocœur, car elle nécessite une synchronisation des threads et de la mémoire pour la pile de chaque thread. Il a également copié les images capturées. En général, mjpg-streamer est devenu suspect # 1.

Trouvaille intéressante


En étudiant mjpg-streamer, j'ai découvert que la capture vidéo sur Linux se fait à l'aide de la bibliothèque v4l2 et une file d'attente de tampon est utilisée pour la capturer. Lors du débogage de l'initialisation de ces tampons dans mjpg-streamer, j'ai trouvé que même pour le mode MJPG, leur taille est très grande et coïncide de manière inattendue avec la taille de la trame non compressée. J'ai donc commencé à soupçonner que je devrais entrer dans le code du pilote UVC, qui est responsable de la prise en charge des caméras.

Analyse du code du conducteur et premier succès


En étudiant le code, je suis arrivé à la conclusion que la taille du tampon est demandée par l'appareil photo et mon appareil photo a renvoyé la taille de l'image non compressée. C'est probablement la solution la plus sûre du point de vue des développeurs de caméras. Mais ce n'est pas non plus optimal. J'ai décidé que pour mon cas, vous pouvez ajuster la taille de tampon requise en utilisant le taux de compression minimum expérimental. J'ai choisi k = 5. Avec cette valeur, j'avais une marge d'environ 20%.

Une petite digression.
, JPG. . , .

Le code du pilote UVC était prêt à ajouter différents types de solutions «spéciales», et j'ai facilement trouvé un endroit pour ajuster la taille du tampon (fonction uvc_fixup_video_ctrl ()). De plus, le pilote prend en charge un ensemble de bizarreries qui vous permettent de prendre en charge des caméras avec divers écarts par rapport à la norme UVC. En général, les développeurs du pilote ont fait de leur mieux pour prendre en charge les caméras du zoo.

En ajoutant une correction de taille de tampon, j'ai obtenu un fonctionnement stable en mode 1280x720 et même en mode 1920x1080. Hourra! La moitié du problème est résolu!

A la recherche de nouvelles aventures


Un peu content du premier succès, je me suis souvenu que mjpg-streamer était loin d'être parfait. Vous pouvez sûrement faire quelque chose de simple, pas aussi universel que mjpg-streamer, mais plus adapté à mes conditions. J'ai donc décidé de faire uvc2http.

Dans mjpg-streamer, je n'aimais pas utiliser plusieurs flux et copier des tampons. Cela a déterminé l'architecture de la solution: 1 flux et aucune copie. En utilisant des E / S non bloquantes, cela se fait tout simplement: capturez la trame et envoyez-la au client sans copier. Il y a un petit problème: pendant que nous envoyons des données depuis le tampon, nous ne pouvons pas retourner le tampon dans la file d'attente. Et tandis que le tampon n'est pas dans la file d'attente, le pilote ne peut pas y mettre une nouvelle trame. Mais si la taille de la file d'attente est> 1, cela devient possible. Le nombre de tampons détermine le nombre maximal de connexions pouvant être garanties à servir. Autrement dit, si je veux prendre en charge 1 client de manière garantie, 3 tampons suffisent (le pilote écrit dans un tampon, les données sont envoyées à partir du second, le troisième est en stock pour éviter de rivaliser avec le pilote pour le tampon lorsque vous essayez d'obtenir une nouvelle trame).

Uvc2http


Uvc2http se compose de deux composants: UvcGrabber et HttpStreamer. Le premier est responsable de la réception des tampons (trames) de la file d'attente et de leur retour dans la file d'attente. Le second est chargé de servir les clients via HTTP. Il existe un peu plus de code qui relie ces composants. Les détails peuvent être trouvés dans la source.

Problème inattendu


Tout était merveilleux: l'application a fonctionné et dans la résolution de 1280x720 a produit plus de 20 images / sec. J'ai apporté des modifications cosmétiques au code. Après un autre lot de modifications, j'ai mesuré la fréquence d'images. Le résultat était déprimant - moins de 15 images. Je me suis précipité pour chercher ce qui avait conduit à la dégradation. J'ai probablement passé 2 heures pendant lesquelles la fréquence diminuait à chaque mesure jusqu'à une valeur de 7 images / sec. Différentes pensées sont entrées dans ma tête à propos de la dégradation due au long travail du routeur, à cause de sa surchauffe. C'était quelque chose d'incompréhensible. À un moment donné, j'ai désactivé le streaming et j'ai vu qu'une seule capture (sans streaming) donnait les mêmes 7 images. J'ai même commencé à soupçonner des problèmes avec l'appareil photo. En général, des bêtises. C'était le soir et la caméra, tournée par la fenêtre, a montré quelque chose de gris. Afin de changer l'image sombre, j'ai tourné la caméra à l'intérieur de la pièce. Et voilà!La fréquence d'images est passée à 15 et j'ai tout compris. L'appareil photo a automatiquement ajusté le temps d'exposition et à un moment donné, ce temps est devenu plus long que la durée de l'image à une fréquence donnée. Pendant ces deux heures, ce qui s'est passé: au début, il faisait peu à peu sombre (c'était le soir), puis j'ai tourné la caméra à l'intérieur de la pièce éclairée. En pointant la caméra vers le lustre, j'ai obtenu plus de 20 images / s. Hourra.


  1. . 1-1.5 .
  2. . , , qv4l2, . : - . .
  3. . USB , ( ) ( ). USB ( ).
  4. Le routeur a très peu de mémoire et d'espace disque. Pour cette raison, j'ai refusé OR-WRT et compilé mon image OpenWRT, en supprimant tout ce qui était superflu.

résultats


Ci-dessous est une plaque avec les résultats de la comparaison de mjpg-streamer et uvc2http. En bref, il y a un gain significatif dans la consommation de mémoire et un petit gain dans la fréquence d'images et l'utilisation du CPU.
1280x7201920x1080
VSZ, KB, 1 clientVSZ, KB, 2 clientsCPU,%, 1 clientCPU,%, 2 clientsFPS, f / s, 1 clientFPS, f / s, 2 clientsVSZ, KB, 1 clientVSZ, KB, 2 clientsCPU,%, 1 clientCPU,%, 2 clientsFPS, f / s, 1 clientFPS, f / s, 2 clients
Mjpg-streamer1686019040264317,6quinze254562581228cinquante13,8dix
uvc2http3960396026432219,675767576284315,512,2

Et bien sûr la vidéo que j'ai faite avec les enfants:



Photo du réservoir résultant (il s'est avéré quelque chose comme une charrette gitane):



En utilisant


Les sources sont ici . Pour une utilisation sur PC Linux, il vous suffit de compiler (à condition que vous ne souhaitiez pas patcher le pilote UVC). L'utilitaire est construit en utilisant CMake de la manière standard. Si vous devez l'utiliser dans OpenWRT, vous devez prendre des mesures supplémentaires:

  1. Copiez le contenu du répertoire OpenWrt-15.05 à la racine du référentiel OpenWRT. Ces fichiers sont pour OpenWRT 15.05 uniquement. Ils décrivent un nouveau package pour OpenWRT et un patch pour le pilote UVC.
  2. , quirk UVC_QUIRK_COMPRESSION_RATE uvc_driver.c. UVC. , wiki.openwrt.org/doc/devel/patches. uvc_ids. :

    /* Logitech B910 HD Webcam */
    	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
    				| USB_DEVICE_ID_MATCH_INT_INFO,
    	  .idVendor		= 0x046d,
    	  .idProduct		= 0x0823,
    	  .bInterfaceClass	= USB_CLASS_VIDEO,
    	  .bInterfaceSubClass	= 1,
    	  .bInterfaceProtocol	= 0,
    	  .driver_info		= UVC_QUIRK_RESTORE_CTRLS_ON_INIT
    				| UVC_QUIRK_COMPRESSION_RATE }, // Enable buffer correction for compressed modes
    

  3. OpenWRT (http://wiki.openwrt.org/doc/howto/build). uvc2http Multimedia.
  4. uvc2http ( ) . , .
  5. Installer le package sur l'appareil / mettre à jour le système

Et après


La solution se compose de deux parties: un correctif de pilote et un autre algorithme de streaming. Le correctif du pilote pourrait être inclus dans la nouvelle version du noyau Linux, mais c'est une décision controversée, car il est basé sur l'hypothèse d'un taux de compression minimum. L'utilitaire, à mon avis, est bien adapté pour une utilisation sur des systèmes faibles (jouets, systèmes de vidéosurveillance à domicile), et il peut être légèrement amélioré en ajoutant la possibilité de spécifier les paramètres de la caméra via les paramètres.

L'algorithme de streaming peut être amélioré car il y a une marge pour le chargement du CPU et la largeur du canal (j'ai facilement reçu 50+ MBit d'un routeur connectant dix clients). Vous pouvez également ajouter un support audio.

Source: https://habr.com/ru/post/fr386273/


All Articles