Las APIs de Transformer en Jetpack Media3 están diseñadas para que la edición de contenido multimedia sea confiable y tenga un buen rendimiento. Transformer admite varias operaciones, incluidas las siguientes:
- Modificar un video con recorte, ajuste de escala y rotación
- Agregar efectos como superposiciones y filtros
- Procesar formatos especiales como video en HDR y en cámara lenta
- Exportar un elemento multimedia después de aplicar ediciones
En esta página, se explican algunos de los casos de uso clave que cubre Transformer. Para obtener más detalles, puedes consultar nuestras guías completas sobre Media3 Transformer.
Comenzar
Para comenzar, agrega una dependencia en los módulos Transformer, Effect y Common de Jetpack Media3:
implementation "androidx.media3:media3-transformer:1.9.2" implementation "androidx.media3:media3-effect:1.9.2" implementation "androidx.media3:media3-common:1.9.2"
Asegúrate de reemplazar 1.9.2 por la versión de la
biblioteca que prefieras. Puedes consultar las
notas de la versión
para ver la versión más reciente.
Clases importantes
| Clase | Propósito |
|---|---|
Transformer |
Inicia y detiene las transformaciones, y verifica las actualizaciones de progreso en una transformación en ejecución. |
EditedMediaItem |
Representa un elemento multimedia para procesar y las ediciones que se le aplicarán. |
Effects |
Es una colección de efectos de audio y video. |
Configura el resultado
Con Transformer.Builder, ahora puedes especificar videoMimeType y
audioMimetype directorio configurando la función sin necesidad de crear un
TransformationRequest objeto.
Transcodifica entre formatos
En el siguiente código, se muestra cómo configurar un objeto Transformer para
generar video H.265/AVC y audio 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();
Si el formato de contenido multimedia de entrada ya coincide con la solicitud de transformación para audio o video, Transformer cambia automáticamente a la transmuxing, es decir, copia las muestras comprimidas del contenedor de entrada al contenedor de salida sin modificaciones. Esto evita el costo computacional y la posible pérdida de calidad de la decodificación y la recodificación en el mismo formato.
Configura el modo HDR
Si el archivo multimedia de entrada está en formato HDR, puedes elegir entre varios
modos diferentes para que Transformer procese la información de HDR. Probablemente
quieras usar HDR_MODE_KEEP_HDR o
HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL.
HDR_MODE_KEEP_HDR |
HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL |
|
|---|---|---|
| Descripción | Conserva los datos de HDR, lo que significa que el formato de salida de HDR es el mismo que el formato de entrada de HDR. | Asigna el tono de la entrada de HDR a SDR con un asignador de tonos de OpenGL, lo que significa que el formato de salida estará en SDR. |
| Asistencia | Se admite en los niveles de API 31 y superiores para dispositivos que incluyen un codificador con la FEATURE_HdrEditing capacidad. |
Se admite en los niveles de API 29 y superiores. |
| Errores | Si no se admite, intenta usar HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL en su lugar. |
Si no se admite, muestra un ExportException. |
En los dispositivos que admiten las capacidades de codificación requeridas y ejecutan Android 13
(nivel de API 33) o versiones posteriores, los objetos Transformer te permiten editar videos HDR.
HDR_MODE_KEEP_HDR es el modo predeterminado cuando se compila el objeto Composition,
como se muestra en el siguiente código:
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();
Prepara un elemento multimedia
Un MediaItem representa un elemento de audio
o video en tu app. Un EditedMediaItem recopila un MediaItem junto
con las transformaciones que se le aplicarán.
Recorta un video
Para quitar las partes no deseadas de un video, puedes configurar las posiciones de inicio y finalización personalizadas agregando un ClippingConfiguration al 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();
Usa efectos integrados
Media3 incluye varios efectos de video integrados para transformaciones comunes, por ejemplo:
| Clase | Efecto |
|---|---|
Presentation |
Ajusta la escala del elemento multimedia por resolución o relación de aspecto. |
ScaleAndRotateTransformation |
Ajusta la escala del elemento multimedia por un multiplicador o rótalo. |
Crop |
Recorta el elemento multimedia a un fotograma más pequeño o más grande. |
OverlayEffect |
Agrega una superposición de texto o imagen sobre el elemento multimedia. |
Para los efectos de audio, puedes agregar una secuencia de
AudioProcessor
instancias que transformarán los datos de audio sin procesar (PCM). Por ejemplo, puedes usar
un ChannelMixingAudioProcessor
para mezclar y ajustar la escala de los canales de audio.
Para usar estos efectos, crea una instancia del efecto o del procesador de audio, compila
una instancia de Effects con los efectos de audio y video que deseas aplicar a
el elemento multimedia y, luego, agrega el objeto Effects a un 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();
Crea efectos personalizados
Si extiendes los efectos incluidos en Media3, puedes crear efectos personalizados
específicos para tus casos de uso. En el siguiente ejemplo, usa la subclase
MatrixTransformation para acercar el video y llenar el fotograma durante el primer
segundo de reproducción:
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 aún más el comportamiento de un efecto, implementa un
GlShaderProgram. El método
queueInputFrame() se usa para procesar fotogramas de entrada. Por ejemplo, para
aprovechar las capacidades de aprendizaje automático de
MediaPipe, puedes usar un
MediaPipe FrameProcessor
para enviar cada fotograma a través de un gráfico de MediaPipe. Consulta un ejemplo de esto en la
app de demostración de Transformer.
Obtén una vista previa de los efectos
Con ExoPlayer, puedes obtener una vista previa de los efectos
agregados a un elemento multimedia antes de iniciar el proceso de exportación. Con el mismo
Effects objeto que para el EditedMediaItem, llama a setVideoEffects() en tu
instancia de 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();
También puedes obtener una vista previa de los efectos de audio con ExoPlayer. Cuando compiles tu
ExoPlayer instancia, pasa un RenderersFactory personalizado que configure los
renderizadores de audio del reproductor para generar audio en un AudioSink que use tu
AudioProcessor secuencia. En el siguiente ejemplo, lo hacemos anulando el
buildAudioSink() método de un 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();
Inicia una transformación
Por último, crea un Transformer para aplicar tus ediciones y comenzar a exportar el
elemento multimedia 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);
Del mismo modo, puedes cancelar el proceso de exportación si es necesario con
Transformer.cancel().
Verifica las actualizaciones de progreso
Transformer.start muestra el resultado de inmediato y se ejecuta de forma asíncrona. Para consultar el
progreso actual de una transformación, llama a
Transformer.getProgress().
Este método toma un ProgressHolder y, si el estado de progreso está disponible,
es decir, si el método muestra PROGRESS_STATE_AVAILABLE, el ProgressHolder proporcionado se actualizará con el porcentaje de progreso actual.
También puedes adjuntar un
objeto de escucha
a tu Transformer para recibir notificaciones sobre eventos de finalización o error.