媒体

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

编解码器

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

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

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


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

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

专有编解码器

专利持有人不会将编解码器授权给仅代表部署给最终用户的最终产品的一部分的软件,例如,像 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())

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

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

视频

JxBrowser 完全支持 HTML5

如果库无法播放视频,或者不支持视频格式,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(AudioStartedPlaying::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();

// Get all available video devices, e.g. web cameras.
List<MediaDevice> videoDevices = mediaDevices.list(MediaDeviceType.VIDEO_DEVICE);

// Get all available audio devices, e.g. microphones.
List<MediaDevice> audioDevices = mediaDevices.list(MediaDeviceType.AUDIO_DEVICE);
val mediaDevices = engine.mediaDevices()

// Get all available video devices, e.g. web cameras.
val videoDevices = mediaDevices.list(MediaDeviceType.VIDEO_DEVICE)

// Get all available audio devices, e.g. microphones.
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 找到潜在的接收器,请使用引擎选项:

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 提供了一个单独的配置文件服务 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.cast(MediaReceiver) 方法:

MediaReceiver receiver = mediaReceivers.await(it -> {
    return it.supports(CastMode.BROWSER);
});
CompletableFuture<CastSession> future = browser.cast(receiver);
val receiver = mediaReceivers.await { it.supports(CastMode.BROWSER) }
val future: CompletableFuture<CastSession> = browser.cast(receiver)

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

默认演示请求

如果网页包含默认的PresentationRequest, 则浏览器开始投射此请求中指定的内容,而不是浏览器的内容。要检查浏览器是否包含默认的 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 -> {
    return it.supports(CastMode.SCREEN);
});
CompletableFuture<CastSession> future = browser.castScreen(receiver);
val receiver = mediaReceivers.await { it.supports(CastMode.SCREEN) }
val future: CompletableFuture<CastSession> = browser.castScreen(receiver)

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

MediaReceiver receiver = mediaReceivers.await(it -> {
    return it.supports(CastMode.SCREEN);
});
Screen screen = profile.mediaCasting().screens().defaultScreen();
ScreenCastOptions options = ScreenCastOptions.newBuilder()
                                             .screen(screen)
                                             .withAudio()
                                             .build();
CompletableFuture<CastSession> future = browser.castScreen(receiver, options);
val receiver = mediaReceivers.await { it.supports(CastMode.SCREEN) }
val screen = profile.mediaCasting().screens().defaultScreen()
val options = ScreenCastOptions.newBuilder()
                               .screen(screen)
                               .withAudio()
                               .build()
val future: CompletableFuture<CastSession> = browser.castScreen(receiver, options)

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

演示API

JxBrowser 允许使用 JavaScript Presentation API.

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

想要向接收器演示,请使用StartPresentationCallback.Action.start(MediaReceiver) 方法:

browser.set(StartPresentationCallback.class, (params, tell) -> {
    MediaReceiver receiver = params.mediaReceivers().await(it -> {
        return it.supports(CastMode.PRESENTATION);
    });
    tell.start(receiver);
});
browser.set(StartPresentationCallback::class.java,
    StartPresentationCallback { params, tell -> 
        val receiver = params.mediaReceivers().await { it.supports(CastMode.PRESENTATION) }
        tell.start(receiver)
    }
)

发现投射会话

为了在发现投射会话时得到通知,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 实例启动的会话。 为了表明这个配置文件已经启动了投射会话,JxBrowser 提供了 CastSession.isLocal() 方法。 因此,如果投射会话由另一个配置文件,甚至另一个 Chromium 进程启动,则该方法将返回 false

停止投射会话

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

CastSession session = profile.mediaCasting().castSessions().list().get(0);
session.on(CastSessionStopped.class, event -> {
    // Do something
});
...
session.stop();
val session = profile.mediaCasting().castSessions().list().first()
session.on(CastSessionStopped::class.java) { event -> 
    // Do something
}
...
session.stop()

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

失败

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

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

Browser.cast...方法返回 CompletableFuture 后,您可以检测到投射会话的启动失败。 在这种情况下,JxBrowser 使用 CastStartFailedException 完成未来:

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