List icon 目录

媒体

本指南概述了支持的视频和音频格式,描述了如何控制音频、获取有关可用网络摄像头和麦克风等的信息。

编解码器

Google Chrome 和 Chromium 在几个方面有所不同,包括它们支持的音频和视频编解码器集合。

下表显示了相应 Browser 的代码库支持的编解码器。

  Chromium Google Chrome
AAC   支持
AV1 支持 支持
FLAC 支持 支持
H.264   支持
HEVC   支持
MP3 支持 支持
Opus 支持 支持
Theora 支持 支持
Vorbis 支持 支持
VP8 支持 支持
VP9 支持 支持
WAV 支持 支持


如您所见,Google Chrome 支持 Chromium 不支持的某些编解码器。原因是这些编解码器是专有的,未经相应专利持有者的许可不能用于开源或商业项目。

不同的编解码器有不同的专利持有者。例如,要使用 H.264,公司必须从 Via LA 公司获得许可。您可以在其网站上阅读有关其许可条款的更多信息。

专有编解码器

专利持有者不会向仅代表最终产品的一部分的软件(例如像 JxBrowser 这样的库)授予编解码器的许可。

为了在您的产品中支持 H.264 和 AAC,您需要获取相应的许可并启用以下专有功能:

Engine engine = Engine.newInstance(
        EngineOptions.newBuilder(renderingMode)
                .enableProprietaryFeature(ProprietaryFeature.AAC)
                .enableProprietaryFeature(ProprietaryFeature.H_264)
                .build());
val engine = Engine.newInstance(
        EngineOptions.newBuilder(renderingMode)
                .enableProprietaryFeature(ProprietaryFeature.AAC)
                .enableProprietaryFeature(ProprietaryFeature.H_264)
                .build())

拥有相应的许可和启用的专有特性后,您将能够像在 Google Chrome 中一样加载带有 AAC 和 H.264 格式的网页,并播放音频和视频文件。默认情况下,这些专有编解码器是禁用的。

重要提示:H.264 和 AAC 编解码器是专有组件。启用这些编解码器,即表示您了解 H.264 和 AAC 是专有组件,您应该持有相应的许可证才能使用它们。有关更多信息,您可以联系专利持有者:Via LA Licensing。TeamDev 对于您如何使用 H.264 和 AAC 编解码器不承担任何责任。

视频

JxBrowser 完全支持 HTML5 的 <video> 元素,可以播放支持格式的视频。

如果库无法播放视频,或者不支持视频格式,JxBrowser 会建议下载该视频文件。有关管理下载的指导,请参阅下载部分。

HTML5 Video

音频

控制音频

使用 Audio,您可以检测当前加载的网页上是否有音频正在播放:

boolean audioPlaying = audio.isPlaying();
val audioPlaying = audio.isPlaying()

您可以在需要时静音或取消静音已加载网页上的音频:

audio.mute();
audio.unmute();
audio.mute()
audio.unmute()

要检查音频是否已静音,请使用以下代码:

boolean audioMuted = audio.isMuted();
val audioMuted = audio.isMuted()

音频事件

要检测已加载的网页上音频是否已开始/停止播放,您可以订阅以下事件:

browser.on(AudioStartedPlaying.class, event -> {});
browser.on(AudioStoppedPlaying.class, event -> {});
browser.on(AudioStartedPlaying::class.java) { event -> }
browser.on(AudioStoppedPlaying::class.java) { event -> }

DRM

Widevine

NetflixAmazon Prime 这样的网络服务使用 Widevine 分发其 DRM 编码内容。Widevine 是一个 Google 专有的组件,默认情况下是禁用的。为了启用它并播放 DRM 编码的内容,请使用以下方法:

Engine engine = Engine.newInstance(
        EngineOptions.newBuilder(renderingMode)
                .enableProprietaryFeature(ProprietaryFeature.WIDEVINE)
                .build());
val engine = Engine.newInstance(
        EngineOptions.newBuilder(renderingMode)
                .enableProprietaryFeature(ProprietaryFeature.WIDEVINE)
                .build())

该库所使用的 Chromium 版本仅支持 Windows 和 macOS 平台上的 Widevine。它在 Linux 上不受支持。一旦 Chromium 在 Linux 上启用对 Widevine 的支持,我们也会在 JxBrowser 中启用它。

重要提示:Widevine 是 Google 的一个专有组件,受其自身使用条款的约束。有关详细信息,请参阅 https://www.widevine.com/

摄像头和麦克风

JxBrowser 支持网络摄像头和麦克风。

您可以使用以下代码获取所有可用的媒体流设备的信息:

MediaDevices mediaDevices = engine.mediaDevices();

// 获取所有可用的视频设备,例如网络摄像头。
List<MediaDevice> videoDevices = mediaDevices.list(MediaDeviceType.VIDEO_DEVICE);

// 获取所有可用的音频设备,例如麦克风。
List<MediaDevice> audioDevices = mediaDevices.list(MediaDeviceType.AUDIO_DEVICE);
val mediaDevices = engine.mediaDevices()

// 获取所有可用的视频设备,例如网络摄像头。
val videoDevices = mediaDevices.list(MediaDeviceType.VIDEO_DEVICE)

// 获取所有可用的音频设备,例如麦克风。
val audioDevices = mediaDevices.list(MediaDeviceType.AUDIO_DEVICE)

您可以使用以下事件检测媒体捕获的开始或停止:

browser.on(MediaStreamCaptureStarted.class, e -> {
    System.out.println("Started capturing " + e.mediaStreamType());
});

browser.on(MediaStreamCaptureStopped.class, e -> {
    System.out.println("Stopped capturing " + e.mediaStreamType());
});
browser.on(MediaStreamCaptureStarted::class.java) { e -> 
    println("Started capturing " + e.mediaStreamType())
}

browser.on(MediaStreamCaptureStopped::class.java) { e -> 
    println("Stopped capturing " + e.mediaStreamType())
}

选择媒体设备

当网页想要使用网络摄像头或麦克风时,您可以使用 SelectMediaDeviceCallback 来告诉网页使用哪个设备。

以下示例演示了如何从可用设备列表中选择第一个设备:

mediaDevices.set(SelectMediaDeviceCallback.class, params -> 
        Response.select(params.mediaDevices().get(0)));
mediaDevices.set(SelectMediaDeviceCallback::class.java,
    SelectMediaDeviceCallback { params ->
        Response.select(params.mediaDevices().first())
    }
)

如果没有所请求类型的媒体输入设备,则不会调用回调。

要禁用对麦克风和网络摄像头的访问,请使用 RequestPermissionCallback,如下所示:

engine.permissions().set(RequestPermissionCallback.class, (params, tell) -> {
    PermissionType type = params.permissionType();
    if (type == PermissionType.VIDEO_CAPTURE || type == PermissionType.AUDIO_CAPTURE) {
        tell.deny();
    } else {
        tell.grant();
    }
});
engine.permissions().set(RequestPermissionCallback::class.java,
    RequestPermissionCallback { params, tell ->
        val type = params.permissionType()
        if (type == PermissionType.VIDEO_CAPTURE || type == PermissionType.AUDIO_CAPTURE) {
            tell.deny()
        } else {
            tell.grant()
        }
    }
)

投射

Chromium 内置了投射功能,可以将媒体内容投射到支持不同无线技术的设备(例如 Chromecast、Miracast、DLNA、AirPlay 或类似技术)。这些设备可以是智能电视、投影仪和其他设备。

The Cast Diagram

预备步骤

默认情况下,我们禁止 Chromium 扫描您的网络以查找媒体设备。要启用此功能并让 Chromium 找到潜在的接收器,请使用 Engine 选项:

EngineOptions options = EngineOptions.newBuilder(renderingMode)
                                     .enableMediaRouting()
                                     .build();
Engine engine = Engine.newInstance(options);
val options = EngineOptions.newBuilder(renderingMode)
                           .enableMediaRouting()
                           .build()
val engine = Engine.newInstance(options)

媒体接收器

要将媒体内容投射到接收器,您需要有一个接收器。为此,JxBrowser 提供了一个单独的 Profile 服务 MediaReceivers,可以通过以下方式获取:

MediaReceivers mediaReceivers = profile.mediaCasting().mediaReceivers();
val mediaReceivers = profile.mediaCasting().mediaReceivers()

为了了解何时发现了新的接收器,JxBrowser 提供了 MediaReceiverDiscovered 事件:

MediaReceivers mediaReceivers = profile.mediaCasting().mediaReceivers();
mediaReceivers.on(MediaReceiverDiscovered.class, event -> {
    MediaReceiver receiver = event.mediaReceiver();
});
val mediaReceivers = profile.mediaCasting().mediaReceivers()
mediaReceivers.on(MediaReceiverDiscovered::class.java) { event -> 
    val receiver = event.mediaReceiver()
}

为了方便您,JxBrowser 会跟踪已发现的接收器。如果要获取当前已发现的媒体接收器的列表,请使用 MediaReceivers.list() 方法:

MediaReceivers mediaReceivers = profile.mediaCasting().mediaReceivers();
List<MediaReceiver> receivers = mediaReceivers.list();
val mediaReceivers: MediaReceivers = profile.mediaCasting().mediaReceivers()
val receivers: List<MediaReceiver> = mediaReceivers.list()

如果您正在寻找特定的接收器,可以通过 MediaReceivers.await(Predicate<MediaReceiver>) 便捷方法获取它。该方法会等待,直到找到第一个与谓词匹配的接收器,然后返回该接收器。

MediaReceivers mediaReceivers = profile.mediaCasting().mediaReceivers();
MediaReceiver receiver = mediaReceivers.await(it -> it.name().equals("Samsung Smart TV"));
val mediaReceivers = profile.mediaCasting().mediaReceivers()
val receiver = mediaReceivers.await { it.name() == "Samsung Smart TV" }

要检测媒体接收器是否已断开连接,即被拔出或与网络断开连接,请使用 MediaReceiverDisconnected 事件:

receiver.on(MediaReceiverDisconnected.class, event -> {
    MediaReceiver mediaReceiver = event.mediaReceiver();
});
receiver.on(MediaReceiverDisconnected::class.java) { event -> 
    val mediaReceiver = event.mediaReceiver()
}

投射内容

JxBrowser API 允许使用 JavaScript Presentation API 投射 Browser、屏幕和演示文稿的内容。

媒体接收器可以支持不同的媒体源。媒体源表示可以投放到媒体接收器上的内容类型。在开始投放之前,请确保所选的媒体接收器支持相应的媒体源。

投射 Browser 内容

要开始投射 Browser 内容,请使用 Browser.cast(MediaReceiver) 方法:

MediaReceiver receiver = mediaReceivers.await(it -> it.name().contains("Samsung"));
if (receiver.supports(MediaSource.browser())) {
    CompletableFuture<CastSession> future = browser.cast(receiver);
}
val receiver = mediaReceivers.await { it.name().contains("Samsung")) }
if (receiver.supports(MediaSource.browser())) {
    val future: CompletableFuture<CastSession> = browser.cast(receiver)
}

将媒体内容投射到媒体接收器的每个会话都由 CastSession 类的实例表示。

默认演示请求

如果网页包含默认的 PresentationRequest,Browser 将开始投射该请求中指定的内容,而不是 Browser 的内容。

要检查 Browser 是否包含默认的 PresentationRequest 请使用:

MediaReceiver receiver = mediaReceivers.await(it -> it.name().contains("Samsung Smart TV"));
browser.defaultPresentationRequest().ifPresent(presentationRequest -> {
    if (receiver.supports(presentationRequest)) {
        CompletableFuture<CastSession> future = browser.cast(receiver);
    }
});
val receiver = mediaReceivers.await { it.name().contains("Samsung Smart TV") }
browser.defaultPresentationRequest().ifPresent {
    if (receiver.supports(it)) {
        val future: CompletableFuture<CastSession> = browser.cast(receiver)
    }
}

投射屏幕内容

要开始投射屏幕内容,请使用 Browser.castScreen(MediaReceiver)。此方法将显示一个标准的 Chromium 对话框,用于选择要投射的屏幕。

MediaReceiver receiver = mediaReceivers.await(it -> it.name().contains("Samsung"));
if (receiver.supports(MediaSource.screen())) {
    CompletableFuture<CastSession> future = browser.castScreen(receiver);
}
val receiver = mediaReceivers.await { it.name().contains("Samsung")) }
if (receiver.supports(MediaSource.screen())) {
    val future: CompletableFuture<CastSession> = browser.castScreen(receiver)
}

如果您想通过编程方式选择屏幕,请使用 Browser.castScreen(MediaReceiver, ScreenCastOptions) 方法。使用 Screens 服务找到所需的屏幕。

MediaReceiver receiver = mediaReceivers.await(it -> it.name().contains("Samsung"));
Screen screen = profile.mediaCasting().screens().defaultScreen();
ScreenCastOptions options = ScreenCastOptions.newBuilder(screen)
                                             .withAudio()
                                             .build();
if (receiver.supports(MediaSource.screen())) {
    CompletableFuture<CastSession> future = browser.castScreen(receiver, options);
}
val receiver = mediaReceivers.await { it.name().contains("Samsung")) }
val screen = profile.mediaCasting().screens().defaultScreen()
val options = ScreenCastOptions.newBuilder(screen)
                               .withAudio()
                               .build()
if (receiver.supports(MediaSource.screen())) {
    val future: CompletableFuture<CastSession> = browser.castScreen(receiver, options)
}

目前,Chromium 仅支持在 Windows 上进行音频投射。因此,在 macOS/Linux 上通过 ScreenCastOptions.Builder.withAudio() 启用音频投射是无效的。在 Windows 上,当在选择器对话框中选择屏幕时,Chromium 提供了一个单独的复选框用于选择音频投射。

Presentation API

JxBrowser 允许使用 JavaScript Presentation API(演示 API)

当在 JavaScript 端调用 PresentationRequest.start() 方法时,JxBrowser 会调用 StartPresentationCallback,您可以在其中决定开始或取消演示。

要将演示投射到接收器,请使用 StartPresentationCallback.Action.start(MediaReceiver) 方法:

browser.set(StartPresentationCallback.class, (params, tell) -> {
    MediaReceiver receiver = params.mediaReceivers().await(it -> {
        return it.name().contains("Samsung");
    });
    if (receiver.supports(params.presentationRequest())) {
        tell.start(receiver);
    } else {
        tell.cancel();
    }
});
browser.set(StartPresentationCallback::class.java,
    StartPresentationCallback { params, tell -> 
        val receiver = params.mediaReceivers().await { it.name().contains("Samsung") }
        if (receiver.supports(params.presentationRequest())) {
            tell.start(receiver)
        } else {
            tell.cancel()
        }
    }
)

发现投射会话

为了在发现投射会话时收到通知,JxBrowser 提供了 CastSessionDiscovered 事件:

profile.mediaCasting().castSessions().on(CastSessionDiscovered.class, event -> {
    CastSession castSession = event.castSession();
});
profile.mediaCasting().castSessions().on(CastSessionDiscovered::class.java) { event -> 
    val castSession = event.castSession()
}

Chromium 可以发现由其他应用程序或 Chromium 实例启动的会话。为了表明投射会话是由当前 Profile 启动的,JxBrowser 提供了 CastSession.isLocal() 方法。因此,如果投射会话由另一个 Profile 甚至另一个 Chromium 进程启动的,则该方法将返回 false

停止投射会话

要停止投射会话,请使用 CastSession.stop() 方法。如果您希望在投射会话停止时收到通知,请使用 CastSessionStopped 事件:

CastSession session = profile.mediaCasting().castSessions().list().get(0);
session.on(CastSessionStopped.class, event -> {
    // 执行一些操作
});
...
session.stop();
val session = profile.mediaCasting().castSessions().list().first()
session.on(CastSessionStopped::class.java) { event -> 
    // 执行一些操作
}
...
session.stop()

会话可以被其他应用程序或 Chromium 实例(例如 Google Chrome)停止。在这种情况下,事件也将被调用。

失败情况

有时,Chromium 可能无法启动新的投射会话,例如,如果媒体接收器未找到或突然断开连接。要检测到这种情况,请使用 CastSessionStartFailed 事件:

MediaReceiver receiver = mediaReceivers.await(it -> it.name().contains("Samsung"));
profile.mediaCasting().castSessions().on(CastSessionStartFailed.class, event -> {
    System.out.println(event.errorMessage());
});
CompletableFuture<CastSession> future = browser.cast(receiver);
val receiver = mediaReceivers.await { it.name().contains("Samsung") }
profile.mediaCasting().castSessions().on(CastSessionStartFailed::class.java) { event ->
    println(event.errorMessage())
}
val future: CompletableFuture<CastSession> = browser.cast(receiver)

这是媒体投射的异步特性所故意设计的一个事件。

由于 Browser.cast...方法返回 CompletableFuture,您可以检测到投射会话的启动已经失败。在这种情况下,JxBrowser 将使用 CastStartFailedException 完成该 future:

CompletableFuture<CastSession> future = browser.cast(receiver);
future.exceptionally(throwable -> {
    System.out.println(throwable.getMessage());
    return null;
});
val future: CompletableFuture<CastSession> = browser.cast(receiver)
future.exceptionally { throwable -> 
    println(throwable.message)
    null
}
Go Top