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.1Dann 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);
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-Kanalmrl = "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.
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).
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 Wagenfür Smartphone