如果您需要在 Java 桌面应用程序中嵌入媒体播放器,有几种方法可供选择:

  • 您可以使用 JavaFX Media API 来实现所有必需的媒体播放器功能。
  • 虽然稍显过时但仍然可用的 Java Media Framework 也可以作为一种解决方案。
  • 您可以集成像 VLCJ 这样的第三方 Java 库,它封装了本机媒体播放器的功能。

每种方法都有其优缺点:

JavaFX Media API 方法是跨平台的,适用于 Windows、Linux 和 macOS。它在 JavaFX 中运作良好。但是,如果您使用 Swing 或 SWT,则需要诸如 JFXPanelFXCanvas 这样的桥接器。

封装本机媒体播放器的功能需要为每个平台单独进行配置,因为播放器可能不支持所有必需的平台。例如,VLCJ 就不支持 Linux。此外,您可能还需要在目标平台上安装缺失的视频和音频编解码器,以播放各种媒体格式。

利用 Web 浏览器的功能

如今,我们大部分的媒体内容都是通过 Web 浏览器来获取的。它们适用于多个平台,并且支持播放各种音频和视频格式。同时,还具备播放媒体内容所需的所有必要功能。既然 Web 浏览器已经如此强大和全面,我们为何不考虑在 Java 桌面应用程序中利用这一优势来播放媒体内容呢?

在本文中,我将介绍另一种可在 Java Swing、JavaFX 或 SWT 应用程序中构建跨平台 Java 媒体播放器的方法。具体操作如下:

  1. 使用 JxBrowser 将 Web 浏览器控件集成到简单的 Java Swing 应用程序中。
  2. 利用 HTML5 的功能加载用于播放所需视频的 HTML 网页。
  3. 通过直接从 Java 代码中调用的 JavaScript 命令来控制视频的播放。

JxBrowser 是一款商业 Java 库,它允许您在跨平台的 Java Swing、JavaFX 和 SWT 应用程序中充分利用 Chromium 的强大功能。非常适合那些基于 Java 技术开发并销售软件解决方案的公司使用。

过去,Flash Player 是我们在网页上展示各种媒体内容的常用工具,备受欢迎。然而,随着技术的不断发展,2020 年 12 月,Flash Player 正式退出了历史舞台,现在,HTML5 的 Video 和 Audio API 已经全面取代了它的地位。

目前,我们主要有两种方式来利用这些 API 播放媒体内容:

  1. 直接使用 HTML5 的 Video 和 Audio API 进行操作。
  2. 使用第三方 JavaScript 库,如 Plyr、Video.js 等。

在本文中,我将使用 Plyr 库 — 这个最受欢迎的 HTML5 媒体播放器之一。它简单易用,与应用程序集成起来非常方便。

使用 JxBrowser 将 Web 功能引入 Java!
获取免费许可证

实现

让我们创建一个演示程序,展示如何使用 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 上获取。

结果

如果您编译并运行该程序,您将会看到以下输出:

Java 媒体播放器演示应用程序

Java 媒体播放器演示应用程序

JxBrowser 是跨平台的,因此这种方法适用于所有平台,无需您付出额外的努力。

结论

HTML5 的视频功能足以构建自定义媒体播放器,以在各种平台上播放大多数流行的视频和音频格式。

希望本文所述方法能对您有所帮助,也期待收到您的宝贵评论和建议。