List icon Conteúdo

Mídia

Este guia apresenta uma visão geral dos formatos de vídeo e áudio suportados, descreve como controlar o áudio, obtém informações sobre webcams e microfones disponíveis, etc.

Codecs

Google Chrome e Chromium diferem de várias formas, incluindo os conjuntos de codecs de áudio e vídeo que suportam.

A tabela abaixo mostra quais os codecs suportados pela base de código dos navegadores correspondentes.

ChromiumGoogle Chrome
AACsim
AV1simsim
FLACsimsim
H.264sim
HEVCsim
MP3simsim
Opussimsim
Theorasimsim
Vorbissimsim
VP8simsim
VP9simsim
WAVsimsim

Como você pode ver, o Google Chrome suporta determinados codecs que o Chromium não suporta. O motivo é que estes codecs são proprietários e não podem ser utilizados num projeto de código aberto ou comercial sem a obtenção de licenças dos titulares das patentes correspondentes.

Os diferentes codecs têm diferentes detentores de patentes. Por exemplo, para utilizar o H.264, as empresas têm de adquirir a licença da empresa Via LA. Você pode ler mais sobre os termos da licença no site Web.

Codecs Proprietários

Os detentores de patentes não licenciam codecs para o software que representa apenas uma parte do produto final implementado para os usuários finais, por exemplo, bibliotecas como o JxBrowser.

Para suportar codecs proprietários nos seus produtos, é necessário adquirir as licenças adequadas e ativar as seguintes funcionalidades proprietárias:

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

Com a licença e as funcionalidades proprietárias ativadas, será possível carregar páginas Web com os formatos AAC, HEVC e H.264 e reproduzir arquivos de áudio e vídeo, tal como no Google Chrome. Por padrão, os codecs proprietários estão desativados.

Importante: Os codecs H.264, HEVC e AAC são os componentes proprietários. Ao ativar estes codecs, você declara ter conhecimento de que H.264, HEVC e AAC são componentes proprietários e que você deve ter uma licença para utilizá-los. Para mais informações, você pode contatar os detentores de patentes: Via LA Licensing. A TeamDev não será responsável pela sua utilização dos codecs H.264, HEVC e AAC.

Video

O JxBrowser suporta totalmente o elemento <video> HTML5 e pode reproduzir vídeo nos formatos suportados.

Se a biblioteca não conseguir reproduzir um vídeo ou se um formato de vídeo não for suportado, o JxBrowser sugere o download do arquivo de vídeo. Consulte Downloads para obter orientações sobre o gerenciamento de downloads.

Vídeo HTML5

Audio

Controle de Áudio

Utilizando Audio é possível saber se o áudio está sendo reproduzido na página web carregada:

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

Se necessário, você pode ativar ou desativar o áudio na página Web carregada:

Java
Kotlin
audio.mute();
audio.unmute();
audio.mute()
audio.unmute()

Para verificar se o áudio está silenciado, utilize o seguinte código:

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

Eventos de Áudio

Para saber se o áudio começou/parou de ser reproduzido na página Web carregada, pode subscrever os seguintes eventos:

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

DRM

Widevine

Os serviços Web como Netflix ou Amazon Prime utilizam Widevine para distribuir os seus conteúdos codificados com DRM. Widevine é um componente propriedade da Google que é desativado por padrão. Para o ativar e reproduzir o conteúdo codificado por DRM, utilize a seguinte abordagem:

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

A versão do Chromium utilizada pela biblioteca suporta o Widevine apenas nas plataformas Windows e macOS. Não é suportado no Linux. Assim que o Chromium ativar o suporte do Widevine no Linux, também o ativaremos no JxBrowser.

Importante: Widevine é um componente proprietário da Google, regido pelos seus próprios termos de utilização. Para mais informações, consultar https://www.widevine.com/.

Câmera e Microfone

O JxBrowser suporta webcam e microfone.

Pode obter informações sobre todos os dispositivos de fluxo multimídia disponíveis utilizando o seguinte código:

Java
Kotlin
MediaDevices mediaDevices = engine.mediaDevices();

// Obter todos os dispositivos de vídeo disponíveis, por exemplo, webcams.
List<MediaDevice> videoDevices = mediaDevices.list(MediaDeviceType.VIDEO_DEVICE);

// Obter todos os dispositivos de áudio disponíveis, por exemplo, microfones.
List<MediaDevice> audioDevices = mediaDevices.list(MediaDeviceType.AUDIO_DEVICE);
val mediaDevices = engine.mediaDevices()

// Obter todos os dispositivos de vídeo disponíveis, por exemplo, webcams.
val videoDevices = mediaDevices.list(MediaDeviceType.VIDEO_DEVICE)

// Obter todos os dispositivos de áudio disponíveis, por exemplo, microfones.
val audioDevices = mediaDevices.list(MediaDeviceType.AUDIO_DEVICE)

É possível detectar quando uma captura de mídia começa ou para de utilizar estes eventos:

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.on(MediaStreamCaptureStarted::class.java) { e -> 
    println("Started capturing " + e.mediaStreamType())
}

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

Seleção do Dispositivo Multimídia

Quando uma página Web deseja utilizar uma webcam ou um microfone, você pode utilizar SelectMediaDeviceCallback para indicar à página Web qual dispositivo utilizar.

O exemplo seguinte demonstra como selecionar o primeiro dispositivo da lista de dispositivos disponíveis:

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

A chamada de retorno não será invocada se não existirem dispositivos de entrada multimídia do tipo solicitado.

Para desativar o acesso a microfones e webcams, utilize RequestPermissionCallback como indicado abaixo:

Java
Kotlin
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()
        }
    }
)

Transmissão

O Chromium tem uma funcionalidade integrada que permite a transmissão de conteúdos multimídia para dispositivos que suportam diferentes tecnologias sem fios, como Chromecast, Miracast, DLNA, AirPlay ou similares. Podem ser smart TVs, projetores e outros dispositivos.

O Diagrama Cast

Etapa Preliminar

Por padrão, desativamos o Chromium de procurar dispositivos multimídia na sua rede. Para ativar e permitir que o Chromium encontre os potenciais receptores, utilize a opção do Motor:

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

Receptores Multimídia

Para começar a transmitir conteúdos multimídia para um receptor, é necessário obter um. Para este efeito, o JxBrowser fornece um serviço de perfil separado MediaReceivers que pode ser obtido desta forma:

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

Para saber quando um novo receptor foi descoberto, o JxBrowser fornece o evento MediaReceiverDiscovered:

Java
Kotlin
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()
}

Para sua conveniência, o JxBrowser mantém um registro dos receptores descobertos. Se desejar obter a lista de receptores multimídia descobertos atualmente, utilize o método MediaReceivers.list():

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

Se estiver à procura de um receptor específico, você pode obtê-lo através do método de conveniência MediaReceivers.await(Predicate<MediaReceiver>). Ele aguarda até que seja descoberto o primeiro receptor que corresponda ao predicado e o retorna.

Java
Kotlin
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" }

Para detectar que o receptor multimídia foi desconectado, ou seja, desligado ou desconectado da rede, utilize o evento MediaReceiverDisconnected:

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

Transmitindo Conteúdo

A API JxBrowser permite a conversão de conteúdos de navegadores, telas e apresentação utilizando a API de apresentação JavaScript.

Os receptores de multimídia podem suportar diferentes fontes de multimídia. Uma fonte multimídia representa um tipo de conteúdo que pode ser transmitido a um receptor multimídia. Antes de iniciar a transmissão, certifique-se de que o receptor multimídia selecionado suporta a fonte multimídia correspondente.

Criando um Navegador

Para começar a transmitir o conteúdo do navegador, utilize o método Browser.cast(MediaReceiver):

Java
Kotlin
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)
}

Cada sessão de transmissão de conteúdos multimídia para um receptor multimídia é representada por uma instância da classe CastSession.

Request de Apresentação Padrão

Se a página Web contiver o PresentationRequest padrão, o browser começa a transmitir o conteúdo especificado neste request ao invés do conteúdo do navegador.

Para verificar se o navegador contém o PresentationRequest padrão, use:

Java
Kotlin
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)
    }
}

Transmitindo uma Tela

Para iniciar a transmissão do conteúdo da tela, utilize Browser.castScreen(MediaReceiver). Este método mostra uma caixa de diálogo padrão do Chromium para escolher a tela a transmitir.

Java
Kotlin
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)
}

Se desejar selecionar a tela de forma programática, utilize o método Browser.castScreen(MediaReceiver, ScreenCastOptions). Encontre a tela que necessita utilizando o serviço Screens:

Java
Kotlin
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)
}

Por enquanto, o Chromium suporta a transmissão de áudio apenas no Windows. Por isso, ativá-lo no macOS/Linux através de ScreenCastOptions.Builder.withAudio() não é uma opção. No Windows, ao selecionar a tela na caixa de diálogo do seletor, o Chromium fornece uma caixa de verificação separada para selecionar a transmissão de áudio.

API de Apresentação

O JxBrowser permite trabalhar com API de Apresentação JavaScript.

Quando o método PresentationRequest.start() é chamado do lado do JavaScript, o JxBrowser invoca o StartPresentationCallback onde você pode decidir iniciar ou cancelar a apresentação.

Para iniciar a apresentação a um receptor, utilize o método StartPresentationCallback.Action.start(MediaReceiver):

Java
Kotlin
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()
        }
    }
)

Descobrindo Sessões de Transmissão

Para ser notificado quando uma sessão de transmissão for descoberta, o JxBrowser fornece o evento CastSessionDiscovered:

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

O Chromium pode descobrir sessões iniciadas por outras aplicações ou instâncias do Chromium. Para indicar que a sessão de transmissão foi iniciada por este perfil, o JxBrowser fornece o método CastSession.isLocal(). Assim, se uma sessão de transmissão for iniciada por outro perfil ou mesmo outro processo do Chromium, o método retornará false.

Interrompendo Sessões de Transmissão

Para parar uma sessão de transmissão, utilize o método CastSession.stop(). Se desejar ser notificado quando uma sessão de transmissão tiver sido interrompida, utilize o evento CastSessionStopped:

Java
Kotlin
CastSession session = profile.mediaCasting().castSessions().list().get(0);
session.on(CastSessionStopped.class, event -> {
    // Faça algo
});
...
session.stop();
val session = profile.mediaCasting().castSessions().list().first()
session.on(CastSessionStopped::class.java) { event -> 
    // Faça algo
}
...
session.stop()

A sessão pode ser interrompida por outras aplicações ou instâncias do Chromium, ou seja Google Chrome. Neste caso, o evento também será invocado.

Falhas

Por vezes, o Chromium pode não conseguir iniciar uma nova sessão de transmissão, ou seja, se o receptor multimídia não for encontrado ou se o tiver sido subitamente desconectado. Para detectar isso, use o evento CastSessionStartFailed:

Java
Kotlin
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)

Trata-se de um acontecimento intencional devido à natureza assíncrona da transmissão de mídia.

Como os métodos Browser.cast... retornam CompletableFuture é possível detectar que o início da sessão de transmissão falhou. Neste caso, o JxBrowser conclui o futuro com a CastStartFailedException:

Java
Kotlin
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
}