利用 Web 浏览器构建 Java Media Player
如果您需要在 Java 桌面应用程序中嵌入媒体播放器,有几种方法可供选择:
- 您可以使用 JavaFX Media API 来实现所有必需的媒体播放器功能。
- 虽然稍显过时但仍然可用的 Java Media Framework 也可以作为一种解决方案。
- 您可以集成像 VLCJ 这样的第三方 Java 库,它封装了本机媒体播放器的功能。
每种方法都有其优缺点:
JavaFX Media API 方法是跨平台的,适用于 Windows、Linux 和 macOS。它在 JavaFX 中运作良好。但是,如果您使用 Swing 或 SWT,则需要诸如 JFXPanel
和 FXCanvas
这样的桥接器。
封装本机媒体播放器的功能需要为每个平台单独进行配置,因为播放器可能不支持所有必需的平台。例如,VLCJ 就不支持 Linux。此外,您可能还需要在目标平台上安装缺失的视频和音频编解码器,以播放各种媒体格式。
利用 Web 浏览器的功能
如今,我们大部分的媒体内容都是通过 Web 浏览器来获取的。它们适用于多个平台,并且支持播放各种音频和视频格式。同时,还具备播放媒体内容所需的所有必要功能。既然 Web 浏览器已经如此强大和全面,我们为何不考虑在 Java 桌面应用程序中利用这一优势来播放媒体内容呢?
在本文中,我将介绍另一种可在 Java Swing、JavaFX 或 SWT 应用程序中构建跨平台 Java 媒体播放器的方法。具体操作如下:
- 使用 JxBrowser[1] 将 Web 浏览器控件集成到简单的 Java Swing 应用程序中。
- 利用 HTML5 的功能加载用于播放所需视频的 HTML 网页。
- 通过直接从 Java 代码中调用的 JavaScript 命令来控制视频的播放。
JxBrowser 是一款商业 Java 库,它允许您在跨平台的 Java Swing、JavaFX 和 SWT 应用程序中充分利用 Chromium 的强大功能。非常适合那些基于 Java 技术开发并销售软件解决方案的公司使用。
过去,Flash Player 是我们在网页上展示各种媒体内容的常用工具,备受欢迎。然而,随着技术的不断发展,2020 年 12 月,Flash Player 正式退出了历史舞台,现在,HTML5 的 Video 和 Audio API 已经全面取代了它的地位。
目前,我们主要有两种方式来利用这些 API 播放媒体内容:
- 直接使用 HTML5 的 Video 和 Audio API 进行操作。
- 使用第三方 JavaScript 库,如 Plyr、Video.js 等。
在本文中,我将使用 Plyr 库 — 这个最受欢迎的 HTML5 媒体播放器之一。它简单易用,与应用程序集成起来非常方便。
实现
让我们创建一个演示程序,展示如何使用 Plyr JS 库和 JxBrowser 构建跨平台的 Java 媒体播放器。
首先,我们需要创建一个 HTML 页面(media.html
),其中包含 JS 库,嵌入视频播放器,并配置目标 MP4 视频文件的位置:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Java Media Player</title>
<link rel="stylesheet" href="<https://cdn.plyr.io/3.6.8/plyr.css>"/>
</head>
<body style="margin:0">
<video id="player"
controls
crossorigin
playsinline
data-poster="<https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg>">
<source src="<https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-720p.mp4>"
type="video/mp4"/>
</video>
<script src="<https://cdn.plyr.io/3.6.8/plyr.js>" crossorigin="anonymous"></script>
</body>
</html>
接下来,我们需要创建一个简单的 Java Swing 应用程序,该程序将显示一个带有 Web 浏览器和播放控件的 JFrame 窗口:
JFrame frame = new JFrame("Java Media Player");
frame.add(new MediaPlayer(), BorderLayout.CENTER);
frame.setVisible(true);
MediaPlayer
组件包含了 Web 浏览器和播放控件。它具有以下初始化逻辑:
engine = Engine.newInstance(
EngineOptions.newBuilder(HARDWARE_ACCELERATED)
// 在这个演示中,我们加载了 MP4 视频文件,
// 因此我们必须启用默认情况下禁用的相应专有功能。
.enableProprietaryFeature(ProprietaryFeature.H_264)
.enableProprietaryFeature(ProprietaryFeature.AAC)
// 启用通过 JavaScript 在网页上无需用户交互即可编程播放视频的功能。
.enableAutoplay()
.build());
Browser browser = engine.newBrowser();
// 将 JsPlayer 的实例注入到 JavaScript 中,
// 以便从 JavaScript 中调用其方法来通知播放器事件。
browser.set(InjectJsCallback.class, params -> {
Frame frame = params.frame();
JsObject window = frame.executeJavaScript("window");
if (window != null) {
player = new JsPlayer(frame);
window.putProperty("java", player);
}
return Response.proceed();
});
// 获取包含 JS 视频播放器的 media.html 文件的绝对路径,
// 加载该文件并等待其完全加载完成,
// 这样我们就可以构建播放器 UI 控件了。
URL resource = MediaPlayer.class.getResource("/media.html");
if (resource != null) {
browser.navigation().loadUrlAndWait(resource.toString());
}
// 创建一个可视化 Swing 控件,
// 用于显示包含视频的网页内容。
BrowserView view = BrowserView.newInstance(browser);
view.setPreferredSize(new Dimension(1280, 720));
// 将控件嵌入到 Java Swing 框架中。
setLayout(new BorderLayout());
add(view, BorderLayout.CENTER);
add(playbackControls(), BorderLayout.SOUTH);
让我来解释一下我在初始化逻辑中都做了什么。在上面的代码中,我配置了 Engine
实例,该实例相当于 Google Chrome 应用程序,并为其设置了几个选项:
- 启用 H264 和 AAC 专有功能,以便能够播放 MP4 视频;
- 启用通过 JavaScript 在网页上无需用户交互即可编程播放视频的功能。
接下来,我创建了一个 Browser
实例,它相当于 Chrome 浏览器的一个标签页,并加载了 media.html
文件。为了显示 HTML 文件的内容,我创建了一个 Swing BrowserView
控件,并将其嵌入到 Java 框架中。
在演示应用程序中,我决定使用以下媒体播放器功能:
- 播放和暂停;
- 静音和取消静音;
- 调节音量;
- 获取视频时长(以秒为单位);
- 当当前播放时间发生变化时接收通知;
- 设置当前播放时间。
对于上述每一种播放功能,我都创建了一个对应的 Java Swing GUI 控件,以便最终的播放面板具有以下外观:
现在,我需要将这些控件与 JS 媒体播放器的相应功能进行绑定。例如,当我点击播放按钮时,我需要调用 player.play()
这个 JS 函数。为此,我使用了相应的 JxBrowser API:
frame.executeJavaScript("player.play()");
为了从 JavaScript 接收播放状态改变的通知,我需要定义一个公开的 Java 类,并在其中使用 @JsAccessible
注解标记公开方法,如下所示:
public final class JsPlayer {
@JsAccessible
public void onTimeUpdated(double currentTime) {
listeners.forEach(listener -> listener.accept(currentTime));
}
}
接着,让我们同样使用以下 JxBrowser API 创建该类的一个实例,并将其注入到 JavaScript 中:
browser.set(InjectJsCallback.class, params -> {
Frame frame = params.frame();
JsObject window = frame.executeJavaScript("window");
if (window != null) {
player = new JsPlayer(frame);
window.putProperty("java", player);
}
return Response.proceed();
});
使用 @JsAccessible
注解的方法在 JavaScript 中是“可见”的,当相应的事件被触发时,可以调用它们。
在该 media.html
文件中,我需要添加 JavaScript 代码,以便在发生不同的播放事件时通知 Java 端:
<script>
player.on('时间更新', event => {
java.onTimeUpdated(player.currentTime);
});
player.on('音量变化', event => {
java.onVolumeChanged(player.volume, player.muted);
});
player.on('播放', event => { java.onPlaybackStarted(); });
player.on('暂停', event => { java.onPlaybackPaused(); });
</script>
该程序的完整源代码以及 media.html
文件可在 GitHub[2] 上获取。
结果
如果您编译并运行该程序,您将会看到以下输出:
JxBrowser 是跨平台的,因此这种方法适用于所有平台,无需您付出额外的努力。
结论
HTML5 的视频功能足以构建自定义媒体播放器,以在各种平台上播放大多数流行的视频和音频格式。
希望本文所述方法能对您有所帮助,也期待收到您的宝贵评论和建议。
参考资料
[1] JxBrowser: https://teamdev.cn/jxbrowser/?utm_campaign=java-media-player&utm_medium=article&utm_source=csdn
[2] GitHub: https://github.com/TeamDev-IP/JxBrowser-Examples/tree/master/tutorials/media-player/src/main?utm_source=csdn&utm_medium=article&utm_campaign=java-media-player