Transmita video desde un dispositivo Android a través de UDP a la aplicación JAVA

Entonces, ve a la línea de meta. Ya hemos aprendido cómo transmitir video desde Android al reproductor VLC, ahora solo queda integrar la ventana con el video en la aplicación JAVA y comenzar a dirigir el robot.



El proyecto de código abierto VLCJ CAPRICA nos ayudará mucho con esto .
El proyecto vlcj proporciona un marco de Java para permitir que una instancia de un reproductor multimedia VLC nativo se incruste en una aplicación Java.
La idea de los chicos es simple, pero ingeniosa (realmente pimienta). En lugar de atormentar con las bibliotecas FFmpeg y más, debe llamar inmediatamente a un especialista como núcleo de un reproductor multimedia VLC normal, funcional y profesional. Y llámelo directamente desde la aplicación JAVA.

A quién le importa, pedimos un gato.

Dado que, en este viaje, hay suficientes dificultades, comenzaremos, como de costumbre, con una muy simple y solo luego pasaremos a lo trivial.

Instalar paquete VLCJ


Primero, verifique su versión del reproductor multimedia VLC. No necesitamos una versión nueva, corta lo que se requiere para la transmisión de udp. Esto ya fue mencionado en una publicación anterior . Por lo tanto, descargamos la versión 2.2.6 de Umbrella y al mismo tiempo revisamos cuidadosamente nuestro paquete JAVA. Deben coincidir en profundidad de bits. Si el reproductor utiliza una arquitectura de 64 bits, entonces el JDK debe ser el mismo. Y no despegará.

Después de eso, ya puede descargar el paquete de la biblioteca VLCJ.



Tenga en cuenta que necesitamos el paquete de distribución (zip) vlcj-3.12.1 . Es él quien trabaja con los jugadores versión VLC 2.2.x. Puede descomprimirlo en cualquier lugar, lo principal es que no está en la carpeta del VLC, porque dos archivos coinciden por nombre. Y si los reescribe, todo esto terminará en un completo fracaso.

A continuación, creamos un proyecto en IntelliJ IDEA IDE (si tiene un IDE diferente, no puedo evitar nada) y escribimos las dependencias necesarias para la integración de las bibliotecas VLCJ.



Hacemos exactamente eso para los archivos:

jna-5.2.0

jna-platform-5.2.0

vlcj-3.12.1

Luego creamos la única clase y escribimos el siguiente pequeño programa en ella.

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

Sí, mientras intentamos reproducir solo un archivo (como se ve en el código). Es mejor no comenzar con udp, no funcionará. Y el archivo se reproduce por completo si, por supuesto, no ha olvidado colocarlo con el nombre correspondiente de antemano cuando sea necesario. Creo que incluso para los javista más novatos no será difícil entender el código anterior.

Todo nuevo es:

llamar para VLCJ

  new NativeDiscovery().discover(); 

y creando la propia instancia del reproductor multimedia

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

Y luego simplemente lo agregamos al panel gráfico deseado:

 contentPane.add(canvas, BorderLayout.CENTER); 

Y todo, el archivo se reproducirá en esta ventana.

Ahora intenta reemplazar

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

en

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

como lo hicimos con calma en la última publicación para transmitir video a través de la conexión udp.
Este número no funcionará aquí. La ventana, por supuesto, se abrirá, pero mostrará un higo, en el sentido de una pantalla oscura. Aunque no habrá registros de errores. Simplemente no habrá nada.

Tengo que resolverlo


¿Quizás falta el códec H264 que seleccionamos en la configuración? ¿Cómo se reprodujo el archivo ttt.mp4? No puede jugar con esta configuración, es mp4.

Inmediatamente llega el entendimiento de que la biblioteca VLCJ ejecuta solo el núcleo del jugador. Y cuáles fueron los preajustes, ella no lo sabe y no quiere saberlo. Es decir, necesitamos, de alguna manera, al iniciar la aplicación JAVA, de alguna manera pasar el reproductor VLC que queremos usar explícitamente el códec H264 o, por ejemplo, queremos rotar la imagen o algo más.

Resulta que esto se puede hacer usando la clase MediaPlayerFactory. Solo lo lanzamos sin argumentos, o incluso con ellos. En stackoverflow.com, inmediatamente encontré un ejemplo simple relacionado con la rotación de imágenes:

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

Es decir, transferimos algo allí con una matriz de cadenas a la fábrica de medios y se almacena allí para el recurso de medios utilizado.

Intenté este método para reproducir el archivo y, como de costumbre, nada funcionó. Resulta que se olvidaron de agregar dos guiones y difundirlo por todo Internet. Tuve que adivinar usando un método de transformación similar.

En resumen, debería ser:

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

¡Ahora nuestro archivo de referencia se ha revertido como debería!



Además será bastante simple:

Para determinar el códec, de acuerdo con la línea de comando VLC, agregamos la línea a la matriz de cadenas:

 "--demux=h264" 

Intentando el canal udp nuevamente


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

Y esta vez todo funciona, lo que indica la victoria de la mente humana. Ahora esta ventana de video o varias de estas ventanas puede portar fácilmente a la interfaz gráfica de su aplicación JAVA.

Parecería una victoria?


En realidad no Un ligero desconcierto fue causado por retrasos temporales o, científicamente, retrasos. Al principio son menos aceptables, pero si tiene la paciencia de ver el video hasta el final, verá que el retraso al final del primer minuto de la transmisión alcanza los cinco segundos. Tuve suficiente paciencia durante 10 minutos de rodaje, pero, curiosamente, el retraso ya no aumentó, sino que se mantuvo dentro de los mismos límites.

el video


Por supuesto, para ver un video desde la cámara, esto servirá, pero difícilmente para controlar un robot. ¡Incluso el rover lunar reaccionó el doble de rápido!

Las sospechas cayeron inmediatamente en los procesos de almacenamiento en caché y éstas (sospechas) resultaron ser ciertas.

Lo más arrogante fue:

  caching for network resources 

Simplemente se come casi todo por defecto, si no se le da a tiempo.
Puede organizar un retraso y:

 caching for cameras and microphones 

Por lo tanto, para evitar retrasos de varios segundos, se recomienda agregar las siguientes líneas a la misma matriz de cadenas, separadas por comas:

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

Los parámetros se establecen allí en milisegundos y, por lo tanto, cualquiera puede elegirlos por sí mismo.

También puedes usar la clave:

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

Luego, el reproductor multimedia intentará optimizar el jitter, moviendo la pantalla. Pero allí, cuanto más tiempo se establece, mejor se optimiza y esto es comprensible por qué. Así que aquí solo queda buscar el consenso y, a veces, ver tanta desgracia en los registros:



Quería, ya sabes, arreglar la inquietud, y estableciste el intervalo de tiempo demasiado pequeño. Ahora es mi culpa.

Ahora todo parece ser como debería. El retraso se redujo a menos de un segundo (aunque un poco menos).

el video


Como resultado, obtuvimos un código de trabajo muy pequeño

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

Ahora puede integrar la transmisión de video en mi programa de control robótico, donde solía transmitir video en pedazos. Y debo decir que el código se ha simplificado enormemente y la calidad ha mejorado en un orden de magnitud. Además de todo a la transmisión de video, podemos transmitir lecturas

acelerómetros
giroscopios
nivel de iluminación
presión de aire
lecturas de la brújula
temperatura
e incluso humedad

Siempre que, por supuesto, todos estos sensores estén disponibles en su teléfono inteligente.

¡E incluso encienda el faro! Automáticamente! Si el nivel de iluminación baja.


Casi nadie está particularmente interesado, pero en caso de un enlace a un github:

para carro
para teléfono inteligente

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


All Articles