Créer une application de montage vidéo basique à l'aide de Media3 Transformer

Les API Transformer de Jetpack Media3 sont conçues pour rendre l'édition multimédia performante et fiable. Transformer accepte un certain nombre d'opérations, y compris :

  • Modifier une vidéo en la coupant, en la redimensionnant et en la faisant pivoter
  • Ajouter des effets tels que des superpositions et des filtres
  • Traitement des formats spéciaux tels que les vidéos HDR et au ralenti
  • Exporter un élément multimédia après avoir appliqué des modifications

Cette page présente certains des principaux cas d'utilisation de Transformer. Pour en savoir plus, consultez nos guides complets sur Media3 Transformer.

Commencer

Pour commencer, ajoutez une dépendance aux modules Transformer, Effect et Common de 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"

Veillez à remplacer 1.11.1 par la version que vous souhaitez utiliser pour la bibliothèque. Vous pouvez consulter les notes de version pour connaître la dernière version.

Classes importantes

Classe Objectif
Transformer Démarrer et arrêter des transformations, et vérifier la progression d'une transformation en cours.
EditedMediaItem Représente un élément multimédia à traiter et les modifications à lui appliquer.
Effects Une collection d'effets audio et vidéo.

Configurer la sortie

Avec Transformer.Builder, vous pouvez désormais spécifier videoMimeType et audioMimetype directement en définissant la fonction sans avoir à créer d'objet TransformationRequest.

Transcoder entre différents formats

Le code suivant montre comment configurer un objet Transformer pour générer une vidéo H.265/AVC et un contenu 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 le format du contenu multimédia d'entrée correspond déjà à la requête de transformation pour l'audio ou la vidéo, Transformer passe automatiquement au transmultiplexage, c'est-à-dire qu'il copie les échantillons compressés du conteneur d'entrée vers le conteneur de sortie sans modification. Cela évite les coûts de calcul et la perte de qualité potentielle liés au décodage et au réencodage dans le même format.

Définir le mode HDR

Si le fichier multimédia d'entrée est au format HDR, vous pouvez choisir entre plusieurs modes de traitement des informations HDR par Transformer. Vous souhaiterez probablement utiliser 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
Description Conserver les données HDR, ce qui signifie que le format de sortie HDR est identique au format d'entrée HDR. Mappez les tons de l'entrée HDR en SDR à l'aide d'un mappeur de tons OpenGL, ce qui signifie que le format de sortie sera en SDR.
Assistance Compatible avec les niveaux d'API 31 et supérieurs pour les appareils incluant un encodeur avec la fonctionnalité FEATURE_HdrEditing. Compatible avec les niveaux d'API 29 et supérieurs.
Erreurs Si ce n'est pas le cas, il tente d'utiliser HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL à la place. Si elle n'est pas compatible, une erreur ExportException est générée.

Sur les appareils compatibles avec les fonctionnalités d'encodage requises et exécutant Android 13 (niveau d'API 33) ou version ultérieure, les objets Transformer vous permettent de modifier des vidéos HDR. HDR_MODE_KEEP_HDR est le mode par défaut lors de la création de l'objet Composition, comme illustré dans le code suivant :

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

Préparer un élément multimédia

Un MediaItem représente un élément audio ou vidéo dans votre application. Un EditedMediaItem collecte un MediaItem ainsi que les transformations à lui appliquer.

Couper une vidéo

Pour supprimer les parties indésirables d'une vidéo, vous pouvez définir des positions de début et de fin personnalisées en ajoutant un ClippingConfiguration au 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();

Utiliser des effets intégrés

Media3 inclut un certain nombre d'effets vidéo intégrés pour les transformations courantes, par exemple :

Classe Effet
Presentation Redimensionner l'élément multimédia en fonction de la résolution ou du format
ScaleAndRotateTransformation Mettre à l'échelle l'élément multimédia à l'aide d'un multiplicateur et/ou faire pivoter l'élément multimédia
Crop Recadrer l'élément multimédia dans un cadre plus petit ou plus grand
OverlayEffect Ajouter une superposition de texte ou d'image sur l'élément multimédia

Pour les effets audio, vous pouvez ajouter une séquence d'instances AudioProcessor qui transformeront les données audio brutes (PCM). Par exemple, vous pouvez utiliser un ChannelMixingAudioProcessor pour mixer et mettre à l'échelle des canaux audio.

Pour utiliser ces effets, créez une instance de l'effet ou du processeur audio, créez une instance de Effects avec les effets audio et vidéo que vous souhaitez appliquer à l'élément multimédia, puis ajoutez l'objet Effects à 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();

Créer des effets personnalisés

En étendant les effets inclus dans Media3, vous pouvez créer des effets personnalisés spécifiques à vos cas d'utilisation. Dans l'exemple suivant, utilisez la sous-classe MatrixTransformation pour zoomer sur la vidéo afin de remplir l'image pendant la première seconde de lecture :

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

Pour personnaliser davantage le comportement d'un effet, implémentez un GlShaderProgram. La méthode queueInputFrame() permet de traiter les images d'entrée. Par exemple, pour exploiter les fonctionnalités de machine learning de MediaPipe, vous pouvez utiliser un FrameProcessor MediaPipe pour envoyer chaque frame via un graphique MediaPipe. Pour en voir un exemple, consultez l'application de démonstration Transformer.

Prévisualiser les effets

Avec ExoPlayer, vous pouvez prévisualiser les effets ajoutés à un élément multimédia avant de lancer le processus d'exportation. En utilisant les mêmes objets Effect que pour EditedMediaItem, appelez setVideoEffects() sur votre instance 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();

Vous pouvez également prévisualiser les effets audio avec ExoPlayer. Lors de la compilation de votre instance ExoPlayer, transmettez un RenderersFactory personnalisé qui configure les moteurs de rendu audio du lecteur pour qu'ils transmettent l'audio à un AudioSink qui utilise votre séquence AudioProcessor. Dans l'exemple ci-dessous, nous procédons en remplaçant la méthode buildAudioSink() d'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();

Lancer une transformation

Enfin, créez un Transformer pour appliquer vos modifications et commencer à exporter l'élément multimédia obtenu.

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

De même, vous pouvez annuler le processus d'exportation si nécessaire avec Transformer.cancel().

Vérifier l'état d'avancement

Transformer.start renvoie immédiatement un résultat et s'exécute de manière asynchrone. Pour interroger la progression actuelle d'une transformation, appelez Transformer.getProgress(). Cette méthode utilise un ProgressHolder. Si l'état de progression est disponible, c'est-à-dire si la méthode renvoie PROGRESS_STATE_AVAILABLE, le ProgressHolder fourni sera mis à jour avec le pourcentage de progression actuel.

Vous pouvez également associer un écouteur à votre Transformer pour être informé des événements de fin ou d'erreur.