Diffusez des vidéos depuis un appareil Android via UDP vers une application JAVA

Alors, allez à la ligne d'arrivée. Nous avons déjà appris à diffuser des vidéos d'Android sur un lecteur VLC, il ne reste plus qu'à intégrer la fenêtre avec la vidéo dans l'application JAVA et à commencer à diriger le robot.



Le projet open source VLCJ CAPRICA nous y aidera grandement.
Le projet vlcj fournit un cadre Java pour permettre à une instance d'un lecteur multimédia VLC natif d'être incorporée dans une application Java.
L'idée des mecs est simple, mais ingénieuse (vraiment du poivre). Au lieu de tourmenter avec les bibliothèques FFmpeg et plus, vous devriez immédiatement appeler un spécialiste au cœur d'un lecteur multimédia VLC normal, fonctionnel et professionnel. Et appelez-le directement depuis l'application JAVA.

Peu importe, nous demandons un chat.

Puisque, dans ce voyage, il y a suffisamment d'écueils, nous commencerons, comme d'habitude, par un très simple et ensuite nous passerons au trivial.

Installer le package VLCJ


Tout d'abord, vérifiez votre version du lecteur multimédia VLC. Nous n'avons pas besoin d'une nouvelle version, elle coupe ce qui est requis pour le flux udp. Cela a déjà été mentionné dans un post précédent . Par conséquent, nous téléchargeons la version 2.2.6 d'Umbrella et vérifions en même temps soigneusement notre package JAVA. Ils doivent correspondre en profondeur de bits. Si le lecteur utilise une architecture 64 bits, le JDK doit être le même. Et ça ne décollera pas.

Après cela, vous pouvez déjà télécharger le package de bibliothèque VLCJ lui-même



Veuillez noter que nous avons besoin du package de distribution (zip) vlcj-3.12.1 . C'est lui qui travaille avec les joueurs version VLC 2.2.x. Vous pouvez le décompresser n'importe où, l'essentiel est qu'il ne se trouve pas dans le dossier du VLC lui-même, car deux fichiers coïncident par leur nom. Et si vous les réécrivez, tout cela se terminera par un échec complet.

Ensuite, nous créons un projet dans IntelliJ IDEA IDE (si vous avez un autre IDE, je ne peux rien faire) et écrivons les dépendances nécessaires pour l'intégration des bibliothèques VLCJ.



C'est exactement ce que nous faisons pour les fichiers:

jna-5.2.0

jna-platform-5.2.0

vlcj-3.12.1

Ensuite, nous créons la seule classe et y écrivons le petit programme suivant.

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

Oui, alors que nous essayons de lire juste un fichier (comme le montre le code). Il vaut mieux ne pas commencer par udp - ça ne marchera pas. Et le fichier est lu complètement si, bien sûr, vous n'avez pas oublié de le placer avec le nom correspondant à l'avance si nécessaire. Je pense que même pour le javista le plus novice, il ne sera pas difficile de comprendre le code ci-dessus.

Tout nouveau c'est:

appel à VLCJ

  new NativeDiscovery().discover(); 

et créer l'instance de lecteur multimédia elle-même

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

Et puis nous l'ajoutons simplement au panneau graphique souhaité:

 contentPane.add(canvas, BorderLayout.CENTER); 

Et tout, le fichier sera lu dans cette fenêtre.

Essayez maintenant de remplacer

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

sur

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

comme nous l'avons fait calmement dans le dernier post pour le streaming vidéo via une connexion udp.
Ce numéro ne fonctionnera pas ici. La fenêtre s'ouvrira, bien sûr, mais elle montrera une figue, dans le sens d'un écran sombre. Bien qu'il n'y ait pas de journaux d'erreurs. Il n'y aura tout simplement rien.

Je dois le comprendre


Peut-être que le codec H264 que nous avons sélectionné dans les paramètres est manquant? Arrêtez, comment alors le fichier ttt.mp4 vient-il de jouer? Il ne peut pas jouer avec ce réglage, il est mp4.

Vient immédiatement la compréhension que la bibliothèque VLCJ exécute uniquement le noyau du lecteur lui-même. Et quels étaient les pré-réglages, elle ne sait pas et ne veut pas savoir. Autrement dit, nous avons besoin, en quelque sorte, lors du lancement de l'application JAVA, en quelque sorte passer le lecteur VLC que nous voulons utiliser explicitement le codec H264 ou, par exemple, nous voulons faire pivoter l'image ou autre chose.

Il s'avère que cela peut être fait en utilisant la classe MediaPlayerFactory. Seulement, nous l'avons lancé sans arguments, ni même avec eux. Sur stackoverflow.com, j'ai tout de suite trouvé un exemple simple lié à la rotation des images:

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

Autrement dit, nous y transférons quelque chose avec un tableau de chaînes vers la fabrique de médias et il y est stocké pour la ressource multimédia utilisée.

J'ai essayé cette méthode pour lire le fichier et comme d'habitude, rien n'a fonctionné. Il s'avère qu'ils ont oublié d'ajouter deux tirets et de les diffuser sur Internet. J'ai dû deviner en utilisant une méthode de transformation similaire.

En bref, ce devrait être:

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

Maintenant, notre fichier de référence est revenu comme il se doit!



De plus, ce sera assez simple:

Pour déterminer le codec, selon la ligne de commande VLC, nous ajoutons la ligne au tableau de chaînes:

 "--demux=h264" 

Réessayer le canal udp


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

Et cette fois, tout fonctionne, indiquant la victoire de l'esprit humain. Maintenant, cette fenêtre vidéo ou plusieurs de ces fenêtres, vous pouvez facilement les porter sur l'interface graphique de votre application JAVA.

Cela semblerait une victoire?


Pas vraiment. Une légère perplexité a été causée par des retards temporaires ou, scientifiquement, des retards. Au début, ils sont moins acceptables, mais si vous avez la patience de regarder la vidéo jusqu'à la fin, vous verrez que le décalage à la fin de la première minute de la diffusion atteint jusqu'à cinq secondes. J'ai eu assez de patience pour 10 minutes de tournage, mais, curieusement, le retard n'a plus augmenté, mais est resté dans les mêmes limites.

la vidéo


Bien sûr, pour regarder une vidéo de la caméra, cela suffira, mais à peine pour contrôler un robot. Même le rover lunaire a réagi deux fois plus vite!

Les soupçons sont immédiatement tombés sur les processus de mise en cache et ils (les soupçons) se sont avérés être vrais.

Le plus arrogant était:

  caching for network resources 

Il mange juste presque tout par défaut, s'il ne lui est pas donné à temps.
Peut organiser un décalage et:

 caching for cameras and microphones 

Par conséquent, pour éviter des retards de plusieurs secondes, il est recommandé d'ajouter les lignes suivantes au même tableau de chaînes, séparées par des virgules:

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

Les paramètres y sont définis en millisecondes et chacun peut donc les choisir par lui-même.

Vous pouvez également utiliser la clé:

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

Ensuite, le lecteur multimédia tentera d'optimiser la gigue - secouant l'écran. Mais là, plus le temps est défini, mieux il est optimisé et cela se comprend pourquoi. Donc, il ne reste plus qu'à chercher un consensus et parfois voir une telle honte dans les journaux:



Il voulait, vous savez, corriger la gigue, et vous définissez l'intervalle de temps trop petit. Maintenant c'est ma faute.

Maintenant, tout semble être comme il se doit. Le délai a été réduit à moins d'une seconde (bien qu'un peu moins).

la vidéo


En conséquence, nous avons obtenu un très petit code de travail

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

Maintenant, vous pouvez intégrer la diffusion vidéo dans mon programme de contrôle robotique, où je transmettais la vidéo en morceaux. Et je dois dire que le code a été grandement simplifié et que la qualité a amélioré d'un ordre de grandeur. En plus de tout au flux vidéo, nous pouvons transmettre des lectures

accéléromètres
gyroscopes
niveau d'éclairage
la pression de l'air
lectures de la boussole
température
et même de l'humidité

A condition, bien entendu, que tous ces capteurs soient disponibles sur votre smartphone.

Et même allumer la lampe frontale! Automatiquement! Si le niveau d'éclairage baisse.


Presque personne n'est particulièrement intéressé, mais en cas de lien vers un github:

pour chariot
pour smartphone

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


All Articles