Video von USB-Kameras auf Linux-Geräten aufnehmen

Hintergrund


Vor einiger Zeit war ich versucht, einen Panzer aus der bekannten Reihe der "Panzerschlachten" zu "verbessern" und die Fähigkeit hinzuzufügen, so zu spielen, als wäre ich ein Panzerfahrer. Die Idee entstand nach dem Lesen mehrerer Artikel über Habré (zum Beispiel hier: geektimes.ru/post/257528), in ihnen fand ich heraus, wie dies mit einem kleinen WLAN-Router und einer USB-Kamera gemacht werden kann. Die Lösung sah faszinierend einfach aus: Der Router wird mit einer speziellen Firmware geflasht, die Kamera wird daran angeschlossen, der Tank wird von der nativen Fernbedienung gesteuert und das Video wird im Browser angezeigt. Nachdem ich den Prototyp schnell zusammengebaut hatte, stellte ich fest, dass das Video in ekelhafter Qualität aufgenommen wurde. Es war entweder 320x440x30 oder 640x480x30. Als der 1280x720-Modus eingeschaltet war, gab es bestenfalls ein zerrissenes Video mit Artefakten, im schlimmsten Fall überhaupt nicht. Der 1920x1080-Modus funktionierte im Prinzip nicht. Das hat mich sehr verärgert, da die Kamera auf einem PC Modi bis 1920x1080x30 unterstützte und Hardware-MJPG-Komprimierung hatte. Meine Intuition deutete darauf hin, dass die Implementierung alles andere als perfekt ist.

Tore


  1. Video in FullHD (1920X1080) oder HD (1280x720) Auflösung und normaler Bildrate (damit Sie abspielen können).
  2. Ich hatte vor, das Spielzeug Kindern zu geben, daher brauchte ich einen automatischen Start und Unterstützung beim Anschließen / Trennen der Kamera.

Im Allgemeinen wollte ich so etwas:



Einschränkungen


Ich wollte nicht nach einer Lösung suchen, die immer und überall funktioniert. Die folgenden Einschränkungen passten perfekt zu mir:

  1. Gutes WLAN-Signal.
  2. Bei einer begrenzten Anzahl von Verbindungen wurde dem Fall Vorrang eingeräumt, wenn nur ein Client vorhanden ist.
  3. Die Kamera unterstützt den MJPG-Modus.

HW und SW


  1. Logitech B910 HD-Camcorder ( http://www.logitech.com/en-us/product/b910-hd-webcam ).
  2. Router TP-LINK TL-MR3020. Dieses Baby hat die folgende Hardware: CPU MIPS 24K 400MHz, RAM 32 MiB, Flash 4 MiB, 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. “ ”.


Im Allgemeinen ist dies eine sehr schwache Konfiguration, insbesondere wenn Sie sich daran erinnern, dass ein Frame im YUV420-Format der Größe 1920 x 1080 4 MiB (2 Byte pro Pixel) benötigt. Ich wurde ermutigt, dass die Kamera Hardware-MJPG-Komprimierung unterstützt. Experimente haben gezeigt, dass ein komprimierter FullHD-Frame typischerweise <500 KiB ist. Also beschloss ich, die Forschung fortzusetzen. Es stellte sich heraus, dass zum Aufnehmen und Streamen von Videos über HTTP mjpg-streamer (http://sourceforge.net/projects/mjpg-streamer/) verwendet wird. Die Analyse seines Codes ergab, dass er 1 Stream zum Aufnehmen von Videos + einen separaten Stream für jeden Client verwendet. Dies ist nicht die beste Lösung für ein Single-Core-System, da für jeden Thread eine Thread-Synchronisation und Speicher für den Stack erforderlich sind. Er kopierte auch aufgenommene Frames. Im Allgemeinen wurde der mjpg-Streamer zum Verdächtigen Nr. 1.

Interessanter Fund


Als ich mjpg-streamer studierte, fand ich heraus, dass die Videoaufnahme unter Linux mit der v4l2-Bibliothek erfolgt und eine Pufferwarteschlange verwendet wird, um sie zu erfassen. Beim Debuggen der Initialisierung dieser Puffer im mjpg-Streamer stellte ich fest, dass ihre Größe selbst im MJPG-Modus sehr groß ist und unerwartet mit der Größe des unkomprimierten Frames übereinstimmt. Also begann ich zu vermuten, dass ich in den UVC-Treibercode gelangen musste, der für die Unterstützung der Kameras verantwortlich ist.

Treibercode-Analyse und erster Erfolg


Als ich den Code studierte, kam ich zu dem Schluss, dass die Größe des Puffers von der Kamera abgefragt wird und meine Kamera die Größe des unkomprimierten Rahmens zurückgibt. Dies ist aus Sicht der Kameraentwickler wahrscheinlich die sicherste Lösung. Es ist aber auch nicht optimal. Ich habe beschlossen, dass Sie für meinen Fall die erforderliche Puffergröße mithilfe des experimentellen Mindestkomprimierungsverhältnisses anpassen können. Ich habe k = 5 gewählt. Mit diesem Wert hatte ich eine Marge von ca. 20%.

Ein kleiner Exkurs.
, JPG. . , .

Der UVC-Treibercode war bereit, verschiedene Arten von „speziellen“ Lösungen hinzuzufügen, und ich fand leicht einen Platz zum Anpassen der Puffergröße (Funktion uvc_fixup_video_ctrl ()). Darüber hinaus unterstützt der Treiber eine Reihe von Macken, mit denen Sie Kameras mit verschiedenen Abweichungen vom UVC-Standard unterstützen können. Im Allgemeinen haben die Entwickler des Treibers das Beste getan, um die Zookameras zu unterstützen.

Durch Hinzufügen einer Puffergrößenkorrektur konnte ich einen stabilen Betrieb im 1280x720-Modus und sogar im 1920x1080-Modus erzielen. Hurra! Das halbe Problem ist gelöst!

Auf der Suche nach neuen Abenteuern


Ein wenig zufrieden mit dem ersten Erfolg, erinnerte ich mich, dass der mjpg-Streamer alles andere als perfekt ist. Sicherlich können Sie etwas Einfaches tun, das nicht so universell ist wie der MJPG-Streamer, aber besser für meine Bedingungen geeignet ist. Also habe ich beschlossen, uvc2http zu machen.

In mjpg-streamer mochte ich es nicht, mehrere Streams zu verwenden und Puffer zu kopieren. Dies bestimmte die Lösungsarchitektur: 1 Stream und keine Kopie. Mit nicht blockierenden E / A-Vorgängen geschieht dies ganz einfach: Erfassen Sie den Frame und senden Sie ihn ohne Kopieren an den Client. Es gibt ein kleines Problem: Während wir Daten aus dem Puffer senden, können wir den Puffer nicht in die Warteschlange zurückgeben. Und während sich der Puffer nicht in der Warteschlange befindet, kann der Treiber keinen neuen Frame in die Warteschlange einfügen. Wenn die Warteschlangengröße jedoch> 1 ist, wird dies möglich. Die Anzahl der Puffer bestimmt die maximale Anzahl der Verbindungen, deren Betrieb garantiert werden kann. Das heißt, wenn ich garantiert 1 Client unterstützen möchte, reichen 3 Puffer aus (der Treiber schreibt in einen Puffer, die Daten werden vom zweiten gesendet, der dritte ist auf Lager, um zu vermeiden, dass der Treiber um den Puffer konkurriert, wenn versucht wird, einen neuen Frame zu erhalten).

Uvc2http


Uvc2http besteht aus zwei Komponenten: UvcGrabber und HttpStreamer. Der erste ist dafür verantwortlich, Puffer (Frames) aus der Warteschlange zu empfangen und an die Warteschlange zurückzugeben. Der zweite ist für die Betreuung von Clients über HTTP verantwortlich. Es gibt noch mehr Code, der diese Komponenten verknüpft. Details finden Sie in der Quelle.

Unerwartetes Problem


Alles war wunderbar: Die Anwendung funktionierte und produzierte in der Auflösung von 1280x720 mehr als 20 Bilder / Sek. Ich habe kosmetische Änderungen am Code vorgenommen. Nach einer weiteren Reihe von Änderungen habe ich die Bildrate gemessen. Das Ergebnis war deprimierend - weniger als 15 Bilder. Ich beeilte mich zu suchen, was zu Erniedrigung führte. Ich habe wahrscheinlich 2 Stunden verbracht, in denen die Frequenz mit jeder Messung auf einen Wert von 7 Bildern / s abnahm. In meinem Kopf kamen verschiedene Gedanken über eine Verschlechterung aufgrund der langen Arbeit des Routers aufgrund seiner Überhitzung auf. Es war etwas Unverständliches. Irgendwann schaltete ich das Streaming aus und sah, dass nur eine Aufnahme (ohne Streaming) die gleichen 7 Frames ergab. Ich fing sogar an, Probleme mit der Kamera zu vermuten. Im Allgemeinen etwas Unsinn. Es war abends und die Kamera, die aus dem Fenster kam, zeigte etwas Graues. Um das düstere Bild zu ändern, drehte ich die Kamera im Raum. Und siehe da!Die Bildrate stieg auf 15 und ich verstand alles. Die Kamera passte die Belichtungszeit automatisch an und irgendwann wurde diese Zeit länger als die Bilddauer bei einer bestimmten Frequenz. Während dieser zwei Stunden passierte Folgendes: Zuerst wurde es allmählich dunkel (es war Abend), und dann drehte ich die Kamera in den beleuchteten Raum. Ich richtete die Kamera auf den Kronleuchter und bekam mehr als 20 Bilder / Sek. Hurra.


  1. . 1-1.5 .
  2. . , , qv4l2, . : - . .
  3. . USB , ( ) ( ). USB ( ).
  4. Der Router hat sehr wenig Speicher und Speicherplatz. Aus diesem Grund lehnte ich OR-WRT ab und kompilierte mein OpenWRT-Image, um alles Überflüssige daraus zu entfernen.

Ergebnisse


Unten ist eine Platte mit den Ergebnissen des Vergleichs von mjpg-streamer und uvc2http. Kurz gesagt, es gibt einen signifikanten Gewinn beim Speicherverbrauch und einen kleinen Gewinn bei der Bildrate und der CPU-Auslastung.
1280 x 7201920 x 1080
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.6fünfzehn254562581228fünfzig13.810
uvc2http3960396026432219.675767576284315.512.2

Und natürlich das Video, das ich mit den Kindern gemacht habe:



Foto des resultierenden Panzers (es stellte sich heraus, dass es sich um einen Zigeunerwagen handelte):



Verwenden von


Quellen sind hier . Für die Verwendung unter PC Linux müssen Sie nur kompilieren (vorausgesetzt, Sie möchten den UVC-Treiber nicht patchen). Das Dienstprogramm wird standardmäßig mit CMake erstellt. Wenn Sie es in OpenWRT verwenden müssen, müssen Sie zusätzliche Schritte ausführen:

  1. Kopieren Sie den Inhalt des OpenWrt-15.05-Verzeichnisses in das Stammverzeichnis des OpenWRT-Repositorys. Diese Dateien sind nur für OpenWRT 15.05. Sie beschreiben ein neues Paket für OpenWRT und einen Patch für den UVC-Treiber.
  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. Installieren Sie das Paket auf dem Gerät / aktualisieren Sie das System

Was weiter


Die Lösung besteht aus zwei Teilen: einem Treiber-Patch und einem weiteren Streaming-Algorithmus. Der Treiber-Patch könnte in der neuen Version des Linux-Kernels enthalten sein, dies ist jedoch eine kontroverse Entscheidung, da sie auf der Annahme eines minimalen Komprimierungsverhältnisses basiert. Das Dienstprogramm eignet sich meiner Meinung nach gut für die Verwendung auf schwachen Systemen (Spielzeug, Heimvideoüberwachungssysteme) und kann leicht verbessert werden, indem die Möglichkeit hinzugefügt wird, Kameraeinstellungen über die Parameter festzulegen.

Der Streaming-Algorithmus kann verbessert werden, da die CPU-Auslastung und die Kanalbreite einen gewissen Spielraum haben (ich habe problemlos mehr als 50 MBit vom Router erhalten, der zehn Clients verbindet). Sie können auch Soundunterstützung hinzufügen.

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


All Articles