Então, vá para a linha de chegada. Já
aprendemos como transmitir vídeo do Android para um VLC player, agora resta apenas integrar a janela com o vídeo no aplicativo JAVA e começar a dirigir o robô.

O projeto de código aberto
VLCJ CAPRICA nos ajudará bastante com
isso .
O projeto vlcj fornece uma estrutura Java para permitir que uma instância de um media player VLC nativo seja incorporada a um aplicativo Java.
A idéia dos caras é simples, mas engenhosa (realmente pimenta). Em vez de atormentar com bibliotecas FFmpeg e mais, chame imediatamente um
especialista como o núcleo de um media player VLC normal, funcional e profissional. E chame-o diretamente do aplicativo JAVA.
Quem se importa, pedimos um gato.
Como nessa viagem existem armadilhas suficientes, começaremos, como sempre, com uma muito simples e só então passaremos para o trivial.
Instale o pacote VLCJ
Primeiro, verifique sua versão do VLC media player. Não precisamos de uma versão nova, ela corta o que é necessário para o fluxo de udp. Isso já foi mencionado em um
post anterior . Portanto,
baixamos a versão
2.2.6 do Umbrella e, ao mesmo tempo, verificamos cuidadosamente nosso pacote JAVA. Eles devem corresponder em profundidade de bits. Se o player usar uma arquitetura de 64 bits, o JDK deverá ser o mesmo. E não vai decolar.
Depois disso, você já pode baixar o próprio
pacote da biblioteca VLCJ

Observe que precisamos do
pacote de distribuição vlcj-3.12.1 (zip) . É ele quem trabalha com os jogadores versão VLC 2.2.x. Você pode descompactá-lo em qualquer lugar, o principal é que ele não está na pasta do VLC, porque dois arquivos coincidem por nome. E se você os reescrever, tudo isso terminará em completo fracasso.
Em seguida, criamos um projeto no IntelliJ IDEA IDE (se você tiver um IDE diferente, não posso ajudar em nada) e escrevemos as dependências necessárias para a integração das bibliotecas VLCJ.

Fazemos exatamente isso para arquivos:
jna-5.2.0
jna-platform-5.2.0
vlcj-3.12.1Em seguida, criamos a única classe e escrevemos o próximo pequeno programa nela.
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);
Sim, enquanto estamos tentando reproduzir apenas um arquivo (como visto no código). É melhor não começar com o udp - não vai funcionar. E o arquivo é reproduzido completamente se, é claro, você não esquecer de colocá-lo com o nome correspondente antecipadamente, quando necessário. Eu acho que mesmo para o javista mais novato, não será difícil entender o código acima.
Tudo novo é:
chamada para VLCJ new NativeDiscovery().discover();
e criando a própria instância do media player mpf = new MediaPlayerFactory(); videoSurface = mpf.newVideoSurface(canvas); MediaPlayer = mpf.newEmbeddedMediaPlayer(); MediaPlayer.setVideoSurface(videoSurface);
Depois, basta adicioná-lo ao painel gráfico desejado:
contentPane.add(canvas, BorderLayout.CENTER);
E tudo, o arquivo será reproduzido nesta janela.
Agora tente substituir
mrl = "D:\\ttt.mp4";
em
mrl = "udp://@:40002";
como fizemos com calma no último post para streaming de vídeo via conexão udp.
Este número não funcionará aqui. A janela, é claro, abrirá, mas mostrará um figo, no sentido de uma tela escura. Embora não haja logs de erros. Simplesmente não haverá nada.
Tenho que descobrir
Talvez o codec H264 que selecionamos nas configurações esteja ausente? Pare, como o arquivo ttt.mp4 foi reproduzido? Ele não pode jogar com essa configuração, ele é mp4.
Imediatamente vem o entendimento de que a biblioteca VLCJ executa apenas o núcleo do player. E quais eram as predefinições, ela não sabe e não quer saber. Ou seja, precisamos, de alguma forma, ao iniciar o aplicativo JAVA, passar de alguma forma o VLC player que queremos usar explicitamente o codec H264 ou, digamos, queremos girar a imagem ou outra coisa.
Acontece que isso pode ser feito usando a classe MediaPlayerFactory. Somente nós o lançamos sem argumentos, ou mesmo com eles. No
stackoverflow.com, encontrei imediatamente um exemplo simples relacionado à rotação de imagens:
String[] args = { "--video-filter", "rotate", "rotate-angle", "10" }; mpf = new MediaPlayerFactory(args);
Ou seja, transferimos algo lá com uma matriz de strings para a fábrica de mídia e ela é armazenada lá para o recurso de mídia usado.
Eu tentei esse método para reproduzir o arquivo e, como de costume, nada funcionou. Acontece que eles esqueceram de adicionar dois traços e espalhá-lo por toda a Internet. Eu tive que adivinhar usando um método de transformação semelhante.
Em suma, deve ser:
String[] args = { "--video-filter", "rotate", "--rotate-angle", "10" };
Agora nosso arquivo de referência foi revertido como deveria!

Além disso, será bastante simples:
Para determinar o codec, de acordo com
a linha de comando do VLC, adicionamos a linha à matriz de strings:
"--demux=h264"
Tentando o canal udp novamentemrl = "udp: // @: 40002";
E desta vez tudo funciona, indicando a vitória da mente humana. Agora, nesta janela de vídeo ou em várias dessas janelas, você pode facilmente portar para a interface gráfica do seu aplicativo JAVA.
Parece uma vitória?
Na verdade não. Uma ligeira perplexidade foi causada por atrasos temporários ou, cientificamente, por atrasos. No início, eles são menos aceitáveis, mas se você tiver paciência para assistir ao vídeo até o fim, verá que o atraso no final do primeiro minuto da transmissão chega a cinco segundos. Eu tive paciência suficiente por 10 minutos de filmagem, mas, estranhamente, o atraso não aumentou mais, mas permaneceu dentro dos mesmos limites.
Claro, para assistir a um vídeo da câmera, isso servirá, mas dificilmente controlar um robô. Até o veículo espacial lunar reagiu duas vezes mais rápido!
As suspeitas caíram imediatamente nos processos de armazenamento em cache e elas (suspeitas) acabaram sendo verdadeiras.
O mais arrogante foi:
caching for network resources
Ele consome quase tudo por padrão, se não for entregue a ele a tempo.
Pode organizar um atraso e:
caching for cameras and microphones
Portanto, para evitar atrasos de vários segundos, é recomendável adicionar as seguintes linhas à mesma matriz de cadeias, separadas por vírgulas:
"--live-caching=100", "--network-caching=500",
Os parâmetros são definidos em milissegundos e, portanto, qualquer um pode escolher por si próprio.
Você também pode usar a chave:
"--clock-jitter=time in milliseconds",
Em seguida, o media player tentará otimizar o tremor - contraindo a tela. Mas, quanto mais tempo for definido, melhor será otimizado e isso será compreensível. Portanto, resta apenas procurar consenso e, às vezes, ver tal desgraça nos logs:

Ele queria, você sabe, consertar a instabilidade, e você ajustou o intervalo de tempo muito pequeno. Agora a culpa é minha.
Agora tudo parece estar como deveria. O atraso foi reduzido para menos de um segundo (embora um pouco menos).
Como resultado, temos um código de trabalho muito pequeno 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); } }
Agora você pode integrar a transmissão de vídeo ao meu programa de controle robótico, onde eu costumava transmitir vídeo em pedaços. E devo dizer que o código foi bastante simplificado e a qualidade melhorou uma ordem de magnitude. Além de tudo no fluxo de vídeo, podemos transmitir leituras
acelerômetros
giroscópios
nível de iluminação
pressão do ar
leituras da bússola
temperatura
e até umidade
Desde que, é claro, todos esses sensores estejam disponíveis no seu smartphone.
E até acender o farol! Automaticamente! Se o nível de iluminação cair.
Quase ninguém está particularmente interessado, mas no caso de um link para um github:
para carrinhopara smartphone