As APIs Transformer no Jetpack Media3 foram projetadas para tornar a edição de mídia eficiente e confiável. O Transformer é compatível com várias operações, incluindo:
- Modificar um vídeo com corte, dimensionamento e rotação
- Adicionar efeitos, como sobreposições e filtros
- Processamento de formatos especiais, como HDR e vídeo em câmera lenta
- Exportar um item de mídia depois de aplicar as edições
Nesta página, mostramos alguns dos principais casos de uso do Transformer. Para mais detalhes, consulte nossos guias completos sobre o Media3 Transformer.
Primeiros passos
Para começar, adicione uma dependência aos módulos Transformer, Effect e Common do Jetpack Media3:
Kotlin
implementation("androidx.media3:media3-transformer:1.11.1")
implementation("androidx.media3:media3-effect:1.11.1")
implementation("androidx.media3:media3-common:1.11.1")
Groovy
implementation "androidx.media3:media3-transformer:1.11.1"
implementation "androidx.media3:media3-effect:1.11.1"
implementation "androidx.media3:media3-common:1.11.1"
Substitua 1.11.1 pela versão de sua preferência da
biblioteca. Consulte as notas da versão para conferir a versão mais recente.
Classes importantes
| Turma | Finalidade |
|---|---|
Transformer |
Inicie e interrompa transformações e verifique as 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 videoMimeType e
audioMimetype diretamente definindo a função sem precisar criar um
objeto TransformationRequest.
Transcodificar entre formatos
O código a seguir 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á corresponder à solicitação de transformação de áudio ou vídeo, o Transformer vai mudar automaticamente para a transmuxação, ou seja, vai copiar as amostras compactadas do contêiner de entrada para o contêiner de saída sem modificações. Isso evita o custo computacional e a possível perda de qualidade da 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 o Transformer processar as informações HDR. Provavelmente,
você vai querer 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 de HDR, ou seja, o formato de saída de HDR é o mesmo que o formato de entrada de HDR. | Mapeia o tom da entrada HDR para SDR usando um mapeador de tons do OpenGL, o que significa que o formato de saída será em SDR. |
| Suporte | Compatível com os níveis da API 31+ para dispositivos que incluem um codificador com o recurso FEATURE_HdrEditing. |
Compatível com os níveis da API 29 ou mais recentes. |
| Erros | Se não houver suporte, tenta usar HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL. |
Se não houver suporte, um ExportException será gerado. |
Em dispositivos que oferecem suporte aos recursos de codificação necessários e executam o Android 13
(nível 33 da API) ou versões mais recentes, 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 a seguir:
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 com as transformações que serão aplicadas
a ele.
Cortar um vídeo
Para remover partes indesejadas de um vídeo, defina posições de início e término personalizadas 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
O Media3 inclui vários efeitos de vídeo integrados para transformações comuns, por exemplo:
| Turma | Efeito |
|---|---|
Presentation |
Dimensionar o item de mídia por resolução ou proporção |
ScaleAndRotateTransformation |
Dimensionar o item de mídia por um multiplicador e/ou girar o item de mídia |
Crop |
Cortar o item de mídia em um frame menor ou maior |
OverlayEffect |
Adicionar uma sobreposição de texto ou imagem ao item de mídia |
Para efeitos de áudio, é possível adicionar uma sequência de instâncias AudioProcessor que
vão transformar os dados de áudio brutos (PCM). Por exemplo, é possível usar um
ChannelMixingAudioProcessor para misturar e dimensionar canais de áudio.
Para usar esses efeitos, crie uma instância do efeito ou do processador de á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 um 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 no Media3, é possível criar efeitos personalizados
específicos para seus casos de uso. No exemplo a seguir, use a subclasse
MatrixTransformation para aumentar o zoom do vídeo e preencher o frame no primeiro
segundo de 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
FrameProcessor do MediaPipe para enviar cada frame por um gráfico
do MediaPipe. Confira um exemplo disso no app de demonstração do Transformer.
Visualizar efeitos
Com o ExoPlayer, é possível conferir os efeitos adicionados a um item de mídia antes de
iniciar o processo de exportação. Usando os mesmos objetos Effect que para o
EditedMediaItem, chame setVideoEffects() na instância do ExoPlayer.
Kotlin
val player = ExoPlayer.Builder(context)
.build()
.also { exoPlayer ->
exoPlayer.setMediaItem(inputMediaItem)
exoPlayer.setVideoEffects(listOf(zoomEffect))
exoPlayer.prepare()
}
Java
ExoPlayer player = new ExoPlayer.Builder(context).build();
player.setMediaItem(inputMediaItem);
player.setVideoEffects(ImmutableList.of(zoomEffect));
player.prepare();
Também é possível conferir os efeitos de áudio com o ExoPlayer. Ao criar a instância
ExoPlayer, transmita um RenderersFactory personalizado que configure os
renderizadores de áudio do player para enviar áudio a um 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 um Transformer para aplicar as edições e começar 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);
Da mesma forma, é possível cancelar o processo de exportação, se necessário, com
Transformer.cancel().
Verificar 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.
Também é possível anexar um listener ao Transformer para receber notificações sobre
eventos de conclusão ou erro.