通过UDP将Android设备中的视频流传输到JAVA应用程序

因此,转到终点线。 我们已经学习了如何将视频从android流传输到VLC播放器,现在只剩下将带有视频的窗口集成到JAVA应用程序中并开始操纵机器人了。



开源项目VLCJ CAPRICA将在此方面极大地帮助我们。
vlcj项目提供了Java框架,以允许将本机VLC媒体播放器的实例嵌入到Java应用程序中。
伙计们的想法很简单,但是很巧妙(真的是辣椒)。 与其使用FFmpeg库及更多功能来烦恼,不如立即将专家称为普通,功能强大且专业的VLC媒体播放器的核心。 并直接从JAVA应用程序调用它。

谁在乎,我们要猫。

既然在这次航行中有足够多的陷阱,我们将像往常一样从一个非常简单的陷阱开始,然后继续进行琐碎的工作。

安装VLCJ软件包


首先,检查您的VLC媒体播放器版本。 我们不需要新版本,它可以减少udp流所需的内容。 在上一篇文章中已经提到了这一点。 因此,我们下载了2.2.6的Umbrella ,同时仔细检查了我们的JAVA软件包。 它们必须在位深度上匹配。 如果播放器使用64位体系结构,则JDK必须相同。 而且它不会起飞。

之后,您已经可以下载VLCJ 库包本身



请注意,我们需要vlcj-3.12.1分发(zip)软件包。 是他与VLC 2.2.x播放器版本一起使用。 您可以将其解压缩到任何地方,主要是因为它不在VLC本身的文件夹中,因为两个文件的名称重合。 而且,如果您重写它们,所有这些都将以完全失败而告终。

接下来,我们在IntelliJ IDEA IDE中创建一个项目(如果您有其他IDE,我无能为力),并为集成VLCJ库编写必要的依赖项。



我们只对文件这样做:

jna-5.2.0

jna平台5.2.0

vlcj-3.12.1

然后,我们创建唯一的类,并在其中编写下一个微型程序。

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

是的,当我们尝试仅播放文件时(从代码中可以看到)。 最好不要从udp开始-它不会起作用。 当然,如果您没有忘记在必要时预先将文件与相应的名称放在一起,则文件将完全播放。 我认为,即使是对于大多数新手javista,也不会很难理解上面的代码。

全新的是:

呼吁VLCJ

  new NativeDiscovery().discover(); 

并创建媒体播放器实例本身

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

然后我们将其添加到所需的图形面板中:

 contentPane.add(canvas, BorderLayout.CENTER); 

一切,文件都将在此窗口中播放。

现在尝试更换

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



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

就像我们在上一篇文章中冷静地通过udp连接流式传输视频一样。
此号码在这里不起作用。 窗口当然会打开,但会显示一个无花果,呈暗屏的感觉。 虽然不会有错误日志。 不会有什么。

要弄清楚


也许我们在设置中选择的H264编解码器丢失了? 停止,然后ttt.mp4文件如何播放? 他无法使用此设置播放,他是mp4。

很快就会明白,VLCJ库仅运行播放器的核心本身。 那里的预设是什么,她不知道也不想知道。 也就是说,我们需要以某种方式在启动JAVA应用程序时以某种方式传递要明确使用H264编解码器的VLC播放器,或者说是要旋转图像或其他方式。

事实证明,可以使用MediaPlayerFactory类来完成此操作。 只有我们在没有参数的情况下甚至没有参数地启动它。 在stackoverflow.com上,我立即发现一个与图像旋转有关的简单示例:

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

也就是说,我们将带有字符串数组的内容传输到媒体工厂,并将其存储在此处以供所使用的媒体资源使用。

我尝试了这种方法来播放文件,并且像往常一样没有任何效果。 事实证明,他们忘记添加两个破折号,并将其分散在整个Internet上。 我不得不猜测使用了类似的转换方法。

简而言之,应为:

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

现在,我们的参考文件已恢复原样!



此外,它将非常简单:

为了确定编解码器,根据VLC命令行,我们将该行添加到字符串数组中:

 "--demux=h264" 

再次尝试udp频道


mrl =“ udp:// @:40002”;

这次一切正常,表明了人类思想的胜利。 现在,您可以轻松地将此视频窗口或几个此类窗口移植到JAVA应用程序的图形界面。

看来胜利了吗?


不完全是 暂时的延迟或科学上的滞后引起了轻微的困惑。 刚开始他们不太可接受,但是如果您有耐心地观看视频直到结束,您将看到到广播的第一分钟结束时的时滞高达五秒钟。 我有足够的耐心进行10分钟的拍摄,但奇怪的是,延迟不再增加了,但仍保持在相同范围内。

影片


当然,对于观看来自摄像机的视频来说,这样做是可以的,但是很难控制机器人。 甚至月球车的反应速度也快两倍!

怀疑立即落入了缓存过程,事实证明它们是对的。

最傲慢的是:

  caching for network resources 

如果没有及时给他,它默认会吃掉几乎所有东西。
可以安排滞后并:

 caching for cameras and microphones 

因此,为避免数秒的延迟,建议将以下行添加到同一字符串数组中,并用逗号分隔:

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

参数以毫秒为单位设置,因此任何人都可以自行选择。

您还可以使用密钥:

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

然后,媒体播放器将尝试优化抖动-抽动屏幕。 但是在那里设置的时间越多,优化的时间就越好,这是可以理解的。 因此,这里仍然只是寻求共识,有时还会在日志中看到这样的耻辱:



他想解决抖动问题,并且将时间间隔设置得太小。 现在是我的错

现在一切似乎都应有。 延迟减少到不到一秒钟(虽然少一点)。

影片


结果,我们得到了非常小的工作代码

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

现在,您可以将视频广播集成到我的机器人控制程序中,该程序过去是我用来分段传输视频的。 我必须说代码已大大简化,质量提高了一个数量级。 除了视频流的所有内容外,我们还可以传输读数

加速度计
陀螺仪
照明水平
气压
指南针读数
温度
甚至湿度

当然,前提是所有这些传感器都可以在智能手机上使用。

甚至打开大灯! 自动! 如果照明水平下降。


几乎没有人特别感兴趣,但是在链接到github的情况下:

购物车
用于智能手机

Source: https://habr.com/ru/post/zh-CN473840/


All Articles