List icon 目录

媒体

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

编解码器

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

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

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

正如您所看到的,Google Chrome 支持某些 Chromium 不支持的编解码器。原因是这些编解码器是专有的,在没有从相应的专利持有者那里获得许可证的情况下,不能在开源或商业项目中使用。

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

专有编解码器

专利持有者不会向仅作为最终产品的一部分部署给最终用户的软件(例如 JxBrowser 这样的库)授权编解码器。

为了在您的产品中支持专有编解码器,您需要获取适当的许可证并启用以下专有功能:

Java
Kotlin

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

val engine = Engine(renderingMode) {
    proprietaryFeatures = setOf(
        ProprietaryFeature.AAC,
        ProprietaryFeature.H_264,
        ProprietaryFeature.HEVC
    )
}

有了许可证并启用了专有功能后,您将能够加载包含 AAC、HEVC 和 H.264 格式的网页,并播放音频和视频文件,就像在 Google Chrome 中一样。默认情况下,专有编解码器是禁用的。

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

视频

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

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

HTML5 Video

音频

控制音频

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

Java
Kotlin

var audioPlaying = audio.isPlaying();

val audioPlaying = audio.isPlaying

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

Java
Kotlin

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

audio.mute()
audio.unmute()

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

Java
Kotlin

var audioMuted = audio.isMuted();

val audioMuted = audio.isMuted

音频事件

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

Java
Kotlin

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

val audio = browser.audio
audio.subscribe<AudioStartedPlaying> { event -> }
audio.subscribe<AudioStoppedPlaying> { event -> }

DRM

Widevine

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

Java
Kotlin

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

val engine = Engine(renderingMode) {
    proprietaryFeatures = setOf(ProprietaryFeature.WIDEVINE)
}

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

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

摄像头和麦克风

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

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

Java
Kotlin

var mediaDevices = engine.mediaDevices();

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

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

val mediaDevices = engine.mediaDevices

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

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

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

Java
Kotlin

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.subscribe<MediaStreamCaptureStarted> { event ->
    println("Started capturing ${event.mediaStreamType()}")
}

browser.subscribe<MediaStreamCaptureStopped> { event ->
    println("Stopped capturing ${event.mediaStreamType()}")
}

选择媒体设备

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

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

Java
Kotlin

mediaDevices.set(SelectMediaDeviceCallback.class, params ->
    Response.select(params.mediaDevices().get(0)));

mediaDevices.register(SelectMediaDeviceCallback { params ->
    val firstDevice = params.mediaDevices().first()
    Response.select(firstDevice)
})

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

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

Java
Kotlin

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

engine.permissions.register(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 选项:

Java
Kotlin

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

val engine = Engine(renderingMode) {
    mediaRoutingEnabled = true
}

媒体接收器

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

Java
Kotlin

var mediaReceivers = profile.mediaCasting().mediaReceivers();

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

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

Java
Kotlin

var mediaReceivers = profile.mediaCasting().mediaReceivers();
mediaReceivers.on(MediaReceiverDiscovered.class, event -> {
    var receiver = event.mediaReceiver();
});

val mediaReceivers = profile.mediaCasting().mediaReceivers()
mediaReceivers.subscribe<MediaReceiverDiscovered> { event ->
    val receiver = event.mediaReceiver()
}

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

Java
Kotlin

var mediaReceivers = profile.mediaCasting().mediaReceivers();
var receivers = mediaReceivers.list();

val mediaReceivers = profile.mediaCasting().mediaReceivers()
val receivers = mediaReceivers.list()

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

Java
Kotlin

var mediaReceivers = profile.mediaCasting().mediaReceivers();
var 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 事件:

Java
Kotlin

receiver.on(MediaReceiverDisconnected.class, event -> {
    var mediaReceiver = event.mediaReceiver();
});

receiver.subscribe<MediaReceiverDisconnected> { event ->
    val mediaReceiver = event.mediaReceiver()
}

投射内容

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

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

投射 Browser 内容

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

Java
Kotlin

var receiver = mediaReceivers.await(it -> it.name().contains("Samsung"));
if (receiver.supports(MediaSource.browser())) {
    var 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 请使用:

Java
Kotlin

var receiver = mediaReceivers.await(it -> it.name().contains("Samsung Smart TV"));
browser.defaultPresentationRequest().ifPresent(request -> {
    if (receiver.supports(request)) {
        var future = browser.cast(receiver);
    }
});

val receiver = mediaReceivers.await { it.name().contains("Samsung") }
browser.defaultPresentationRequest().ifPresent { request ->
    if (receiver.supports(request)) {
        val future = browser.cast(receiver)
    }
}

投射屏幕内容

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

Java
Kotlin

var receiver = mediaReceivers.await(it -> it.name().contains("Samsung"));
if (receiver.supports(MediaSource.screen())) {
    var 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 服务找到所需的屏幕。

Java
Kotlin

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

val receiver = mediaReceivers.await { it.name().contains("Samsung") }
val screen = profile.mediaCasting().screens().defaultScreen()
val options = ScreenCastOptions(screen, withAudio = true)
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) 方法:

Java
Kotlin

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

browser.register(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 事件:

Java
Kotlin

profile.mediaCasting().castSessions().on(CastSessionDiscovered.class, event -> {
    var castSession = event.castSession();
});

val castSessions = profile.mediaCasting().castSessions()
castSessions.subscribe<CastSessionDiscovered> { event ->
    val castSession = event.castSession()
}

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

停止投射会话

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

Java
Kotlin

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

val session = profile.mediaCasting().castSessions().list().first()
session.subscribe<CastSessionStopped> { event ->
    // 执行一些操作。
}
...
session.stop()

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

失败情况

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

Java
Kotlin

var receiver = mediaReceivers.await(it -> it.name().contains("Samsung"));
profile.mediaCasting().castSessions().on(CastSessionStartFailed.class, event ->
    System.out.println(event.errorMessage()));
var future = browser.cast(receiver);

val receiver = mediaReceivers.await { it.name().contains("Samsung") }
val castSessions = profile.mediaCasting().castSessions()
castSessions.subscribe<CastSessionStartFailed> { event ->
    println(event.errorMessage())
}
val future: CompletableFuture<CastSession> = browser.cast(receiver)

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

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

Java
Kotlin

var 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
}