The Transformer APIs in Jetpack Media3 are designed to make media editing
performant and reliable. Transformer supports a number of operations,
including:

- Modifying a video with trimming, scaling, and rotating
- Adding effects like overlays and filters
- Processing special formats like HDR and slow-motion video
- Exporting a media item after applying edits

This page walks you through some of the key use cases covered by
Transformer. For more details you can head to our full guides on
[Media3 Transformer](https://developer.android.com/guide/topics/media/transformer).

## Get started

To get started, add a dependency on the Transformer, Effect, and Common modules
of Jetpack Media3:  

```groovy
implementation "androidx.media3:media3-transformer:1.8.0"
implementation "androidx.media3:media3-effect:1.8.0"
implementation "androidx.media3:media3-common:1.8.0"
```

Make sure to replace `1.8.0` with your preferred version of the
library. You can refer to the
[release notes](https://github.com/androidx/media/tree/release/RELEASENOTES.md)
to see the latest version.

### Important classes

|                                                 Class                                                  |                                          Purpose                                           |
|--------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| [Transformer](https://developer.android.com/reference/androidx/media3/transformer/Transformer)         | Start and stop transformations and check for progress updates on a running transformation. |
| [EditedMediaItem](https://developer.android.com/reference/androidx/media3/transformer/EditedMediaItem) | Represents a media item to process and the edits to apply to it.                           |
| [Effects](https://developer.android.com/reference/androidx/media3/transformer/Effects)                 | A collection of audio and video effects.                                                   |

## Configure the output

With `Transformer.Builder`, you can now specify `videoMimeType` and
`audioMimetype` directory by setting the function without needing to create a
`TransformationRequest` object.

### Transcode between formats

The following code shows how to configure a `Transformer` object to
output H.265/AVC video and AAC audio:  

### Kotlin

```kotlin
val transformer = Transformer.Builder(context)
    .setVideoMimeType(MimeTypes.VIDEO_H265)
    .setAudioMimeType(MimeTypes.AUDIO_AAC)
    .build()
```

### Java

```java
Transformer transformer = new Transformer.Builder(context)
    .setVideoMimeType(MimeTypes.VIDEO_H265)
    .setAudioMimeType(MimeTypes.AUDIO_AAC)
    .build();
```

If the input media format already matches the transformation request for audio
or video, Transformer automatically switches to *transmuxing*, that is, copying
the compressed samples from the input container to the output container without
modification. This avoids the computational cost and potential quality loss of
decoding and re-encoding in the same format.

### Set HDR mode

If the input media file is in an HDR format, you can choose between a few
different modes for how Transformer processes the HDR information. You probably
want to use either `HDR_MODE_KEEP_HDR` or
`HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL`.

|             |                                                                                                  `HDR_MODE_KEEP_HDR`                                                                                                   |                                             `HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL`                                              |
|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
| Description | Preserve the HDR data, meaning that the HDR output format is the same as the HDR input format.                                                                                                                         | Tonemap HDR input to SDR using an OpenGL tone-mapper, meaning that the output format will be in SDR.                                  |
| Support     | Supported on API levels 31+ for devices that include an encoder with the [`FEATURE_HdrEditing`](https://developer.android.com/reference/android/media/MediaCodecInfo.CodecCapabilities#FEATURE_HdrEditing) capability. | Supported on API levels 29+.                                                                                                          |
| Errors      | If not supported, attempts to use `HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL` instead.                                                                                                                                | If not supported, throws an [`ExportException`](https://developer.android.com/reference/androidx/media3/transformer/ExportException). |

On devices that support the required encoding capabilities and run Android 13
(API level 33) or higher, `Transformer` objects let you edit HDR videos.
`HDR_MODE_KEEP_HDR` is the default mode when building the `Composition` object,
as shown in the following code:  

### Kotlin

```kotlin
val composition = Composition.Builder(
    ImmutableList.of(videoSequence))
    .setHdrMode(HDR_MODE_KEEP_HDR)
    .build()
```

### Java

```java
Composition composition = new Composition.Builder(
    ImmutableList.of(videoSequence))
    .setHdrMode(Composition.HDR_MODE_KEEP_HDR)
    .build();
```

## Prepare a media item

A [`MediaItem`](https://developer.android.com/guide/topics/media/exoplayer/media-items) represents an audio
or video item in your app. An `EditedMediaItem` collects a `MediaItem` along
with the transformations to apply to it.

### Trim a video

To remove unwanted portions of a video, you can set custom start and end
positions by adding a `ClippingConfiguration` to the `MediaItem`.  

### Kotlin

```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

```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();
```

### Use built-in effects

Media3 includes a number of built-in video effects for common transformations,
for example:

|                                                             Class                                                             |                                                                                                        Effect                                                                                                        |
|-------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`Presentation`](https://developer.android.com/reference/androidx/media3/effect/Presentation)                                 | Scale the media item by resolution or aspect ratio                                                                                                                                                                   |
| [`ScaleAndRotateTransformation`](https://developer.android.com/reference/androidx/media3/effect/ScaleAndRotateTransformation) | Scale the media item by a multiplier and/or rotate the media item                                                                                                                                                    |
| [`Crop`](https://developer.android.com/reference/androidx/media3/effect/Crop)                                                 | Crop the media item to a smaller or larger frame                                                                                                                                                                     |
| [`OverlayEffect`](https://developer.android.com/reference/androidx/media3/effect/OverlayEffect)                               | Add a [text](https://developer.android.com/reference/androidx/media3/effect/TextOverlay) or [image](https://developer.android.com/reference/androidx/media3/effect/DrawableOverlay) overlay on top of the media item |

For audio effects, you can add a sequence of
[`AudioProcessor`](https://developer.android.com/reference/androidx/media3/common/audio/AudioProcessor)
instances that will transform the raw (PCM) audio data. For example, you can use
a [`ChannelMixingAudioProcessor`](https://developer.android.com/reference/androidx/media3/common/audio/ChannelMixingAudioProcessor)
to mix and scale audio channels.

To use these effects, create an instance of the effect or audio processor, build
an instance of `Effects` with the audio and video effects you want to apply to
the media item, then add the `Effects` object to an `EditedMediaItem`.  

### Kotlin

```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

```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();
```

### Create custom effects

By extending the effects included in Media3, you can create custom effects
specific to your use cases. In the following example, use subclass
`MatrixTransformation` to zoom the video into filling the frame over the first
second of playback:  

### Kotlin

```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

```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();
```

To further customize the behavior of an effect, implement a
[`GlShaderProgram`](https://developer.android.com/reference/androidx/media3/effect/GlShaderProgram). The
`queueInputFrame()` method is used to process input frames. For example, to
leverage the machine learning capabilities of
[MediaPipe](https://developers.google.com/mediapipe), you can use a
MediaPipe [`FrameProcessor`](https://github.com/google/mediapipe/tree/master/mediapipe/java/com/google/mediapipe/components/FrameProcessor.java)
to send each frame through a MediaPipe graph. See an example of this in the
[Transformer demo app](https://github.com/androidx/media/tree/release/demos/transformer).

### Preview effects

With [ExoPlayer](https://developer.android.com/guide/topics/media/exoplayer), you can preview the effects
added to a media item before starting the export process. Using the same
`Effects` object as for the `EditedMediaItem`, call `setVideoEffects()` on your
ExoPlayer instance.  

### Kotlin

```kotlin
val player = ExoPlayer.builder(context)
    .build()
    .also { exoPlayer ->
        exoPlayer.setMediaItem(inputMediaItem)
        exoPlayer.setVideoEffects(effects)
        exoPlayer.prepare()
    }
```

### Java

```java
ExoPlayer player = new ExoPlayer.builder(context).build();
player.setMediaItem(inputMediaItem);
player.setVideoEffects(effects);
exoPlayer.prepare();
```

You can also preview audio effects with ExoPlayer. When building your
`ExoPlayer` instance, pass in a custom `RenderersFactory` that configures the
player's audio renderers to output audio to an `AudioSink` that uses your
`AudioProcessor` sequence. In the example below, we do this by overriding the
`buildAudioSink()` method of a `DefaultRenderersFactory`.  

### Kotlin

```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

```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();
```

## Start a transformation

Lastly, create a `Transformer` to apply your edits and start exporting the
resulting media item.  

### Kotlin

```kotlin
val transformer = Transformer.Builder(context)
    .addListener(listener)
    .build()
transformer.start(editedMediaItem, outputPath)
```

### Java

```java
Transformer transformer = new Transformer.Builder(context)
    .addListener(listener)
    .build();
transformer.start(editedMediaItem, outputPath);
```

You can similarly cancel the export process if needed with
[`Transformer.cancel()`](https://developer.android.com/reference/androidx/media3/transformer/Transformer#cancel()).

### Check for progress updates

`Transformer.start` returns immediately and runs asynchronously. To query the
current progress of a transformation, call
[`Transformer.getProgress()`](https://developer.android.com/reference/androidx/media3/transformer/Transformer#getProgress(androidx.media3.transformer.ProgressHolder)).
This method takes a `ProgressHolder`, and if the progress state is available,
that is, if the method returns `PROGRESS_STATE_AVAILABLE`, then the provided
`ProgressHolder` will be updated with the current progress percentage.

You can also attach a
[listener](https://developer.android.com/reference/androidx/media3/transformer/Transformer.Listener)
to your `Transformer` to be notified about completion or error events.