As APIs Transformer no Jetpack Media3 foram projetadas para melhorar o desempenho e a confiabilidade da edição de mídia. O Transformer é compatível com várias operações, incluindo:
- Modificar um vídeo cortando, dimensionando e girando
- adicionar efeitos, como sobreposições e filtros;
- Processamento de formatos especiais, como HDR e vídeos em câmera lenta
- Exportar um item de mídia após aplicar edições
Nesta página, você verá alguns dos principais casos de uso cobertos pelo Transformer. Para mais detalhes, acesse nossos guias completos no Media3 Transformer.
Começar
Para começar, adicione uma dependência aos módulos Transformer, Effect e Common do Jetpack Media3:
implementation "androidx.media3:media3-transformer:1.3.1" implementation "androidx.media3:media3-effect:1.3.1" implementation "androidx.media3:media3-common:1.3.1"
Substitua 1.3.1
pela versão de sua preferência da
biblioteca. Consulte as notas da versão para saber qual é a versão mais recente.
Aulas importantes
Classe. | Objetivo |
---|---|
Transformer |
Inicie e pare transformações e verifique se há atualizações de progresso em uma transformação em execução. |
EditedMediaItem |
Representa um item de mídia a ser processado e as edições a serem aplicadas a ele. |
Effects |
Uma coleção de efeitos de áudio e vídeo. |
Configurar a saída
Com Transformer.Builder
, agora é possível especificar o diretório videoMimeType
e
audioMimetype
definindo a função sem precisar criar um
objeto TransformationRequest
.
Transcodificação entre formatos
O código abaixo mostra como configurar um objeto Transformer
para
gerar vídeo H.265/AVC e áudio AAC:
Kotlin
val transformer = Transformer.Builder(context) .setVideoMimeType(MimeTypes.VIDEO_H265) .setAudioMimeType(MimeTypes.AUDIO_AAC) .build()
Java
Transformer transformer = new Transformer.Builder(context) .setVideoMimeType(MimeTypes.VIDEO_H265) .setAudioMimeType(MimeTypes.AUDIO_AAC) .build();
Se o formato de mídia de entrada já corresponde à solicitação de transformação de áudio ou vídeo, o Transformer alterna automaticamente para transmuxing, ou seja, copiando as amostras compactadas do contêiner de entrada para o contêiner de saída sem modificação. Isso evita o custo computacional e a potencial perda de qualidade na decodificação e recodificação no mesmo formato.
Definir o modo HDR
Se o arquivo de mídia de entrada estiver em um formato HDR, você poderá escolher entre alguns
modos diferentes para a forma como o Transformer processa as informações HDR. É recomendável usar HDR_MODE_KEEP_HDR
ou HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL
.
HDR_MODE_KEEP_HDR |
HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL |
|
---|---|---|
Descrição | preservar os dados HDR, o que significa que o formato de saída HDR é igual ao formato de entrada HDR. | Mapeie a entrada HDR para SDR usando um tone-mapper OpenGL, o que significa que o formato de saída será em SDR. |
Suporte | Tem suporte no nível 31 ou mais recente da API para dispositivos que incluem um codificador com o recurso FEATURE_HdrEditing . |
Compatível com APIs de nível 29 ou mais recente. |
Erros | Se não houver suporte, ele tentará usar HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL . |
Se não for compatível, uma ExportException será gerada. |
Em dispositivos com suporte aos recursos de codificação necessários e que executam o Android 13
(nível 33 da API) ou mais recente, os objetos Transformer
permitem editar vídeos em HDR.
HDR_MODE_KEEP_HDR
é o modo padrão ao criar o objeto Composition
,
conforme mostrado no código abaixo:
Kotlin
val composition = Composition.Builder( ImmutableList.of(videoSequence)) .setHdrMode(HDR_MODE_KEEP_HDR) .build()
Java
Composition composition = new Composition.Builder( ImmutableList.of(videoSequence)) .setHdrMode(Composition.HDR_MODE_KEEP_HDR) .build();
Preparar um item de mídia
Um MediaItem
representa um item
de áudio ou vídeo no app. Um EditedMediaItem
coleta um MediaItem
junto
com as transformações a serem aplicadas a ele.
Cortar um vídeo
Para remover partes indesejadas de um vídeo, defina posições personalizadas de início e fim
adicionando um ClippingConfiguration
ao MediaItem
.
Kotlin
val clippingConfiguration = MediaItem.ClippingConfiguration.Builder() .setStartPositionMs(10_000) // start at 10 seconds .setEndPositionMs(20_000) // end at 20 seconds .build() val mediaItem = MediaItem.Builder() .setUri(videoUri) .setClippingConfiguration(clippingConfiguration) .build()
Java
ClippingConfiguration clippingConfiguration = new MediaItem.ClippingConfiguration.Builder() .setStartPositionMs(10_000) // start at 10 seconds .setEndPositionMs(20_000) // end at 20 seconds .build(); MediaItem mediaItem = new MediaItem.Builder() .setUri(videoUri) .setClippingConfiguration(clippingConfiguration) .build();
Usar efeitos integrados
A Media3 inclui vários efeitos de vídeo integrados para transformações comuns, por exemplo:
Classe. | Efeito |
---|---|
Presentation |
Dimensionar o item de mídia por resolução ou proporção |
ScaleAndRotateTransformation |
Dimensionar o item de mídia em um multiplicador e/ou girá-lo |
Crop |
Cortar o item de mídia em um frame menor ou maior |
OverlayEffect |
Adicionar uma sobreposição de texto ou imagem na parte superior do item de mídia |
Para efeitos de áudio, adicione uma sequência de instâncias AudioProcessor
que transformarão os dados de áudio brutos (PCM, na sigla em inglês). Por exemplo, você pode usar
um ChannelMixingAudioProcessor
para mixar e dimensionar canais de áudio.
Para usar esses efeitos, crie uma instância do processador de efeito ou áudio, crie
uma instância de Effects
com os efeitos de áudio e vídeo que você quer aplicar ao
item de mídia e adicione o objeto Effects
a uma EditedMediaItem
.
Kotlin
val channelMixingProcessor = ChannelMixingAudioProcessor() val rotateEffect = ScaleAndRotateTransformation.Builder().setRotationDegrees(60f).build() val cropEffect = Crop(-0.5f, 0.5f, -0.5f, 0.5f) val effects = Effects(listOf(channelMixingProcessor), listOf(rotateEffect, cropEffect)) val editedMediaItem = EditedMediaItem.Builder(mediaItem) .setEffects(effects) .build()
Java
ChannelMixingAudioProcessor channelMixingProcessor = new ChannelMixingAudioProcessor(); ScaleAndRotateTransformation rotateEffect = new ScaleAndRotateTransformation.Builder() .setRotationDegrees(60f) .build(); Crop cropEffect = new Crop(-0.5f, 0.5f, -0.5f, 0.5f); Effects effects = new Effects( ImmutableList.of(channelMixingProcessor), ImmutableList.of(rotateEffect, cropEffect) ); EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem) .setEffects(effects) .build();
Criar efeitos personalizados
Ao estender os efeitos incluídos na Media3, você pode criar efeitos personalizados
específicos para seus casos de uso. No exemplo abaixo, use a subclasse
MatrixTransformation
para ampliar o vídeo e aumentar o frame do frame no primeiro
segundo da reprodução:
Kotlin
val zoomEffect = MatrixTransformation { presentationTimeUs -> val transformationMatrix = Matrix() // Set the scaling factor based on the playback position val scale = min(1f, presentationTimeUs / 1_000f) transformationMatrix.postScale(/* x */ scale, /* y */ scale) transformationMatrix } val editedMediaItem = EditedMediaItem.Builder(inputMediaItem) .setEffects(Effects(listOf(), listOf(zoomEffect)) .build()
Java
MatrixTransformation zoomEffect = presentationTimeUs -> { Matrix transformationMatrix = new Matrix(); // Set the scaling factor based on the playback position float scale = min(1f, presentationTimeUs / 1_000f); transformationMatrix.postScale(/* x */ scale, /* y */ scale); return transformationMatrix; }; EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(inputMediaItem) .setEffects(new Effects(ImmutableList.of(), ImmutableList.of(zoomEffect))) .build();
Para personalizar ainda mais o comportamento de um efeito, implemente um
GlShaderProgram
. O
método queueInputFrame()
é usado para processar frames de entrada. Por exemplo, para
aproveitar os recursos de machine learning do
MediaPipe, use um
MediaPipe FrameProcessor
para enviar cada frame por um gráfico do MediaPipe. Veja um exemplo no app de demonstração do Transformer.
Visualizar efeitos
Com o ExoPlayer, é possível visualizar os efeitos
adicionados a um item de mídia antes de iniciar o processo de exportação. Usando o mesmo objeto
Effects
da EditedMediaItem
, chame setVideoEffects()
na
instância do ExoPlayer.
Kotlin
val player = ExoPlayer.builder(context) .build() .also { exoPlayer -> exoPlayer.setMediaItem(inputMediaItem) exoPlayer.setVideoEffects(effects) exoPlayer.prepare() }
Java
ExoPlayer player = new ExoPlayer.builder(context).build(); player.setMediaItem(inputMediaItem); player.setVideoEffects(effects); exoPlayer.prepare();
Você também pode testar efeitos de áudio com o ExoPlayer. Ao criar sua
instância ExoPlayer
, transmita um RenderersFactory
personalizado que configure os
renderizadores de áudio do player para emitir áudio para uma AudioSink
que use sua
sequência AudioProcessor
. No exemplo abaixo, fazemos isso substituindo o
método buildAudioSink()
de um DefaultRenderersFactory
.
Kotlin
val player = ExoPlayer.Builder(context, object : DefaultRenderersFactory(context) { override fun buildAudioSink( context: Context, enableFloatOutput: Boolean, enableAudioTrackPlaybackParams: Boolean, enableOffload: Boolean ): AudioSink? { return DefaultAudioSink.Builder(context) .setEnableFloatOutput(enableFloatOutput) .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) .setOffloadMode(if (enableOffload) { DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED } else { DefaultAudioSink.OFFLOAD_MODE_DISABLED }) .setAudioProcessors(arrayOf(channelMixingProcessor)) .build() } }).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context, new DefaultRenderersFactory(context) { @Nullable @Override protected AudioSink buildAudioSink( Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams, boolean enableOffload ) { return new DefaultAudioSink.Builder(context) .setEnableFloatOutput(enableFloatOutput) .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) .setOffloadMode( enableOffload ? DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED : DefaultAudioSink.OFFLOAD_MODE_DISABLED) .setAudioProcessors(new AudioProcessor[]{channelMixingProcessor}) .build(); } }).build();
Iniciar uma transformação
Por fim, crie uma Transformer
para aplicar suas edições e comece a exportar o
item de mídia resultante.
Kotlin
val transformer = Transformer.Builder(context) .addListener(listener) .build() transformer.start(editedMediaItem, outputPath)
Java
Transformer transformer = new Transformer.Builder(context) .addListener(listener) .build(); transformer.start(editedMediaItem, outputPath);
É possível cancelar o processo de exportação da mesma forma, se necessário, com
Transformer.cancel()
.
Verificar se há atualizações de progresso
Transformer.start
retorna imediatamente e é executado de forma assíncrona. Para consultar o
progresso atual de uma transformação, chame
Transformer.getProgress()
.
Esse método usa um ProgressHolder
e, se o estado de progresso estiver disponível,
ou seja, se o método retornar PROGRESS_STATE_AVAILABLE
, o
ProgressHolder
fornecido será atualizado com a porcentagem de progresso atual.
Você também pode anexar um
listener
ao Transformer
para receber notificações sobre eventos de conclusão ou erro.