因此,转到终点线。 我们已经
学习了如何将视频从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);
是的,当我们尝试仅播放文件时(从代码中可以看到)。 最好不要从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的情况下:
购物车用于智能手机