Streamen Sie Videos vom Android-Gerät über UDP zur JAVA-Anwendung

Also, geh bis zur Ziellinie. Wir haben bereits gelernt, wie man Videos vom Android zum VLC-Player streamen kann. Jetzt muss nur noch das Fenster mit dem Video in die JAVA-Anwendung integriert und der Roboter gesteuert werden.



Das Open-Source-Projekt VLCJ CAPRICA wird uns dabei sehr helfen.
Das vlcj-Projekt bietet ein Java-Framework, mit dem eine Instanz eines nativen VLC-Mediaplayers in eine Java-Anwendung eingebettet werden kann.
Die Idee der Jungs ist einfach, aber genial (wirklich Pfeffer). Anstatt mit FFmpeg-Bibliotheken und mehr zu quälen, sollten Sie sofort einen Spezialisten als Kern eines normalen, funktionalen und professionellen VLC-Mediaplayers bezeichnen. Und rufen Sie es direkt aus der JAVA-Anwendung auf.

Wen kümmert es, wir bitten um eine Katze.

Da es auf dieser Reise genügend Fallstricke gibt, werden wir wie üblich mit einer sehr einfachen beginnen und erst dann zum Trivialen übergehen.

Installieren Sie das VLCJ-Paket


Überprüfen Sie zunächst Ihre Version des VLC Media Players. Wir brauchen keine neue Version, sie schneidet, was für den UDP-Stream erforderlich ist. Dies wurde bereits in einem früheren Beitrag erwähnt . Daher laden wir die Version 2.2.6 von Umbrella herunter und überprüfen gleichzeitig sorgfältig unser JAVA-Paket. Sie müssen in der Bittiefe übereinstimmen. Wenn der Player eine 64-Bit-Architektur verwendet, muss das JDK identisch sein. Und es wird nicht abheben.

Danach können Sie bereits das VLCJ- Bibliothekspaket selbst herunterladen



Bitte beachten Sie, dass wir das Distributionspaket vlcj-3.12.1 (zip) benötigen. Er arbeitet mit der Spielerversion VLC 2.2.x. Sie können es überall entpacken. Hauptsache, es befindet sich nicht im Ordner des VLC selbst, da zwei Dateien nach Namen übereinstimmen. Und wenn Sie sie neu schreiben, wird dies alles zu einem völligen Misserfolg führen.

Als Nächstes erstellen wir ein Projekt in der IntelliJ IDEA IDE (wenn Sie eine andere IDE haben, kann ich nichts helfen) und schreiben die erforderlichen Abhängigkeiten für die Integration von VLCJ-Bibliotheken.



Wir machen genau das für Dateien:

jna-5.2.0

jna-platform-5.2.0

vlcj-3.12.1

Dann erstellen wir die einzige Klasse und schreiben das nächste kleine Programm darin.

import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import uk.co.caprica.vlcj.component.EmbeddedMediaPlayerComponent; import uk.co.caprica.vlcj.discovery.NativeDiscovery; import uk.co.caprica.vlcj.player.MediaPlayerFactory; import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer; import uk.co.caprica.vlcj.player.embedded.videosurface.CanvasVideoSurface; public class BasicPlayer { public final JFrame frame; public static String mrl; public static MediaPlayerFactory mpf; public static EmbeddedMediaPlayer MediaPlayer; public static CanvasVideoSurface videoSurface; public static Canvas canvas; public static void main(final String[] args) { new NativeDiscovery().discover(); mrl = "D:\\ttt.mp4"; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { BasicPlayer vp = new BasicPlayer(); vp.start(mrl); } }); } public BasicPlayer() { frame = new JFrame("My First Media Player"); frame.setBounds(200, 100, 540, 340); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.out.println(e); MediaPlayer.release(); mpf.release(); System.exit(0); } }); JPanel contentPane = new JPanel(); contentPane.setLayout(new BorderLayout()); canvas = new Canvas(); mpf = new MediaPlayerFactory(); videoSurface = mpf.newVideoSurface(canvas); MediaPlayer = mpf.newEmbeddedMediaPlayer(); MediaPlayer.setVideoSurface(videoSurface); contentPane.add(canvas, BorderLayout.CENTER); //        frame.setContentPane(contentPane); frame.setVisible(true); } public void start(String mrl) { MediaPlayer.playMedia(mrl); } } 

Ja, während wir versuchen, nur eine Datei abzuspielen (wie aus dem Code ersichtlich). Es ist besser, nicht mit udp zu beginnen - es wird nicht funktionieren. Und die Datei wird vollständig abgespielt, wenn Sie natürlich nicht vergessen haben, sie bei Bedarf im Voraus mit dem entsprechenden Namen zu platzieren. Ich denke, dass es selbst für den Anfänger Javista nicht schwierig sein wird, den obigen Code zu verstehen.

Alles neu ist:

Anruf für VLCJ

  new NativeDiscovery().discover(); 

und Erstellen der Media Player-Instanz selbst

  mpf = new MediaPlayerFactory(); videoSurface = mpf.newVideoSurface(canvas); MediaPlayer = mpf.newEmbeddedMediaPlayer(); MediaPlayer.setVideoSurface(videoSurface); 

Und dann fügen wir es einfach dem gewünschten Grafikfeld hinzu:

 contentPane.add(canvas, BorderLayout.CENTER); 

Und alles, die Datei wird in diesem Fenster abgespielt.

Versuchen Sie nun zu ersetzen

 mrl = "D:\\ttt.mp4"; 

auf

 mrl = "udp://@:40002"; 

wie wir es ruhig im letzten Beitrag für das Streaming von Videos über udp-Verbindung getan haben.
Diese Nummer funktioniert hier nicht. Das Fenster öffnet sich natürlich, aber es zeigt eine Feige im Sinne eines dunklen Bildschirms. Es werden zwar keine Fehlerprotokolle angezeigt. Es wird einfach nichts geben.

Ich muss es herausfinden


Möglicherweise fehlt der H264-Codec, den wir in den Einstellungen ausgewählt haben? Stop, wie wurde dann die Datei ttt.mp4 gerade abgespielt? Er kann mit dieser Einstellung nicht spielen, er ist mp4.

Es entsteht sofort das Verständnis, dass in der VLCJ-Bibliothek nur der Kern des Players selbst ausgeführt wird. Und was die Voreinstellungen dort waren, weiß sie nicht und will es nicht wissen. Das heißt, wir müssen beim Starten der JAVA-Anwendung irgendwie den VLC-Player übergeben, den wir explizit mit dem H264-Codec verwenden möchten, oder wir möchten beispielsweise das Bild oder etwas anderes drehen.

Es stellt sich heraus, dass dies mit der MediaPlayerFactory-Klasse erfolgen kann. Nur wir haben es ohne Argumente oder sogar mit ihnen gestartet. Auf stackoverflow.com habe ich sofort ein einfaches Beispiel für die Bildrotation gefunden:

 String[] args = { "--video-filter", "rotate", "rotate-angle", "10" }; mpf = new MediaPlayerFactory(args); 

Das heißt, wir übertragen dort etwas mit einem String-Array an die Media Factory und es wird dort für die verwendete Medienressource gespeichert.

Ich habe diese Methode ausprobiert, um die Datei abzuspielen, und wie üblich hat nichts funktioniert. Es stellt sich heraus, dass sie vergessen haben, zwei Striche hinzuzufügen und im ganzen Internet zu verbreiten. Ich musste mit einer ähnlichen Transformationsmethode raten.

Kurz gesagt sollte es sein:

 String[] args = { "--video-filter", "rotate", "--rotate-angle", "10" }; 

Jetzt wurde unsere Referenzdatei wie gewünscht zurückgesetzt!



Weiter wird es ganz einfach sein:

Um den Codec gemäß der VLC-Befehlszeile zu bestimmen , fügen wir die Zeile dem Zeichenfolgenarray hinzu:

 "--demux=h264" 

Ich versuche es erneut mit dem udp-Kanal


mrl = "udp: // @: 40002";

Und diesmal funktioniert alles und zeigt den Sieg des menschlichen Geistes an. Mit diesem oder mehreren solchen Fenstern können Sie nun problemlos auf die grafische Oberfläche Ihrer JAVA-Anwendung portieren.

Es scheint ein Sieg zu sein?


Nicht wirklich. Eine leichte Verwirrung wurde durch vorübergehende Verzögerungen oder wissenschaftlich Verzögerungen verursacht. Anfangs sind sie mehr oder weniger akzeptabel, aber wenn Sie die Geduld haben, das Video bis zum Ende anzusehen, werden Sie feststellen, dass die Verzögerung am Ende der ersten Minute der Sendung bis zu fünf Sekunden beträgt. Ich hatte genug Geduld für 10 Minuten Drehzeit, aber seltsamerweise nahm die Verzögerung nicht mehr zu, sondern blieb innerhalb der gleichen Grenzen.

das Video


Um ein Video von der Kamera anzusehen, reicht dies natürlich aus, aber kaum, um einen Roboter zu steuern. Sogar der Mondrover reagierte doppelt so schnell!

Der Verdacht fiel sofort auf die Caching-Prozesse und sie (Verdächtigungen) erwiesen sich als wahr.

Das arroganteste war:

  caching for network resources 

Es frisst standardmäßig fast alles auf, wenn es ihm nicht rechtzeitig gegeben wird.
Kann eine Verzögerung arrangieren und:

 caching for cameras and microphones 

Um Verzögerungen von mehreren Sekunden zu vermeiden, wird empfohlen, dem folgenden Zeichenfolgenarray die folgenden Zeilen hinzuzufügen, die durch Kommas getrennt sind:

  "--live-caching=100", "--network-caching=500", 

Die Parameter werden dort in Millisekunden eingestellt und können daher von jedem selbst ausgewählt werden.

Sie können auch den Schlüssel verwenden:

 "--clock-jitter=time in milliseconds", 

Dann versucht der Media Player, den Jitter zu optimieren und den Bildschirm zucken. Aber je mehr Zeit eingestellt ist, desto besser wird sie optimiert, und dies ist verständlich, warum. Hier bleibt es also nur, einen Konsens zu suchen und manchmal eine solche Schande in den Protokollen zu sehen:



Er wollte den Jitter beheben, und Sie haben das Zeitintervall zu klein eingestellt. Jetzt ist es meine Schuld.

Jetzt scheint alles so zu sein, wie es sollte. Die Verzögerung wurde auf weniger als eine Sekunde reduziert (wenn auch etwas weniger).

das Video


Als Ergebnis haben wir einen sehr kleinen Arbeitscode erhalten

 import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import uk.co.caprica.vlcj.discovery.NativeDiscovery; import uk.co.caprica.vlcj.player.MediaPlayerFactory; import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer; import uk.co.caprica.vlcj.player.embedded.videosurface.CanvasVideoSurface; public class BasicVideoPlayer { public final JFrame frame; public static String mrl; public static MediaPlayerFactory mpf; public static EmbeddedMediaPlayer MediaPlayer; public static CanvasVideoSurface videoSurface; public static Canvas canvas; public static void main(final String[] args) { new NativeDiscovery().discover(); mrl = "udp://@:40002"; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { BasicVideoPlayer vp = new BasicVideoPlayer(); vp.start(mrl); } }); } public BasicVideoPlayer() { frame = new JFrame("My First Media Player"); frame.setBounds(200,100, 540, 340); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.out.println(e); MediaPlayer.release(); mpf.release(); System.exit(0); } }); JPanel contentPane = new JPanel(); contentPane.setLayout(new BorderLayout()); canvas = new Canvas(); String[] args = { "--video-filter", "rotate", "--rotate-angle", "270", "--demux=h264", "--clock-jitter=100", "--live-caching=100", "--network-caching=500", }; mpf = new MediaPlayerFactory(args); videoSurface = mpf.newVideoSurface(canvas); MediaPlayer = mpf.newEmbeddedMediaPlayer(); MediaPlayer.setVideoSurface(videoSurface); contentPane.add(canvas, BorderLayout.CENTER); frame.setContentPane(contentPane); frame.setVisible(true); } public void start(String mrl) { MediaPlayer.playMedia(mrl); } } 

Jetzt können Sie die Videoübertragung in mein Robotersteuerungsprogramm integrieren, in dem ich Videos in Teilen übertragen habe. Und ich muss sagen, der Code wurde stark vereinfacht und die Qualität um eine Größenordnung verbessert. Zusätzlich zu allem, was zum Videostream gehört, können wir Messwerte übertragen

Beschleunigungsmesser
Gyroskope
Beleuchtungsstärke
Luftdruck
Kompassablesungen
Temperatur
und sogar Feuchtigkeit

Vorausgesetzt natürlich, dass alle diese Sensoren auf Ihrem Smartphone verfügbar sind.

Und sogar den Scheinwerfer einschalten! Automatisch! Wenn die Beleuchtungsstärke sinkt.


Kaum jemand ist besonders interessiert, aber im Falle eines Links zu einem Github:

für Wagen
für Smartphone

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


All Articles