Einfache Videobearbeitungsanwendung mit Media3 Transformer erstellen

Die Transformer APIs in Jetpack Media3 wurden für eine leistungsstarke und zuverlässige Medienbearbeitung entwickelt. Transformer unterstützt eine Reihe von Vorgängen, darunter:

  • Video mit Zuschneiden, Skalieren und Drehen bearbeiten
  • Effekte wie Overlays und Filter hinzufügen
  • Spezielle Formate wie HDR- und Zeitlupenvideos verarbeiten
  • Mediendatei nach dem Bearbeiten exportieren

Auf dieser Seite werden einige der wichtigsten Anwendungsfälle beschrieben, die von Transformer abgedeckt werden. Weitere Informationen finden Sie in unseren vollständigen Leitfäden zu Media3 Transformer.

Jetzt starten

Fügen Sie zuerst eine Abhängigkeit von den Modulen „Transformer“, „Effect“ und „Common“ von Jetpack Media3 hinzu:

implementation "androidx.media3:media3-transformer:1.9.3"
implementation "androidx.media3:media3-effect:1.9.3"
implementation "androidx.media3:media3-common:1.9.3"

Ersetzen Sie 1.9.3 durch die gewünschte Version der Bibliothek. Die aktuelle Version finden Sie in den Versionshinweisen.

Wichtige Klassen

Klasse Zweck
Transformer Transformationen starten und beenden und nach Fortschrittsupdates für eine laufende Transformation suchen
EditedMediaItem Stellt eine zu verarbeitende Mediendatei und die darauf anzuwendenden Änderungen dar.
Effects Eine Sammlung von Audio- und Videoeffekten

Ausgabe konfigurieren

Mit Transformer.Builder können Sie jetzt das Verzeichnis videoMimeType und audioMimetype angeben, indem Sie die Funktion festlegen, ohne ein TransformationRequest-Objekt erstellen zu müssen.

Transcodierung zwischen Formaten

Der folgende Code zeigt, wie Sie ein Transformer-Objekt so konfigurieren, dass es H.265/AVC-Video und AAC-Audio ausgibt:

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

Wenn das Eingabemedienformat bereits mit der Transformationsanfrage für Audio oder Video übereinstimmt, wechselt Transformer automatisch zum Transmuxing. Dabei werden die komprimierten Samples unverändert vom Eingabe- in den Ausgabecontainer kopiert. So werden die Rechenkosten und der potenzielle Qualitätsverlust durch Decodierung und erneute Codierung im selben Format vermieden.

HDR-Modus festlegen

Wenn die Eingabemediendatei in einem HDR-Format vorliegt, können Sie zwischen verschiedenen Modi auswählen, wie Transformer die HDR-Informationen verarbeitet. Wahrscheinlich möchten Sie entweder HDR_MODE_KEEP_HDR oder HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL verwenden.

HDR_MODE_KEEP_HDR HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL
Beschreibung Die HDR-Daten werden beibehalten. Das HDR-Ausgabeformat ist also dasselbe wie das HDR-Eingabeformat. HDR-Eingabe wird mit einem OpenGL-Tone-Mapper in SDR umgewandelt. Das Ausgabeformat ist also SDR.
Support Wird auf API-Ebene 31 und höher für Geräte unterstützt, die einen Encoder mit der FEATURE_HdrEditing Funktion enthalten. Wird auf API-Ebene 29 und höher unterstützt.
Fehler Wird der Modus nicht unterstützt, wird stattdessen versucht, HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL zu verwenden. Wird der Modus nicht unterstützt, wird ExportException ausgelöst.

Auf Geräten, die die erforderlichen Codierungsfunktionen unterstützen und auf denen Android 13 (API-Ebene 33) oder höher ausgeführt wird, können Sie mit Transformer-Objekten HDR-Videos bearbeiten. HDR_MODE_KEEP_HDR ist der Standardmodus beim Erstellen des Composition-Objekts, wie im folgenden Code gezeigt:

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

Mediendatei vorbereiten

Ein MediaItem stellt eine Audio oder Videodatei in Ihrer App dar. Ein EditedMediaItem enthält ein MediaItem zusammen mit den darauf anzuwendenden Transformationen.

Video zuschneiden

Wenn Sie unerwünschte Teile eines Videos entfernen möchten, können Sie benutzerdefinierte Start- und Endpositionen festlegen, indem Sie dem MediaItem eine ClippingConfiguration hinzufügen.

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

Integrierte Effekte verwenden

Media3 enthält eine Reihe integrierter Videoeffekte für gängige Transformationen, z. B.:

Klasse Effekte
Presentation Mediendatei nach Auflösung oder Seitenverhältnis skalieren
ScaleAndRotateTransformation Mediendatei mit einem Multiplikator skalieren und/oder drehen
Crop Mediendatei auf einen kleineren oder größeren Rahmen zuschneiden
OverlayEffect Text- oder Bild-Overlays über der Mediendatei einblenden

Für Audioeffekte können Sie eine Sequenz von AudioProcessor-Instanzen hinzufügen, die die PCM-Rohaudiodaten transformieren. Sie können beispielsweise einen ChannelMixingAudioProcessor verwenden, um Audio-Channels zu mischen und zu skalieren.

Wenn Sie diese Effekte verwenden möchten, erstellen Sie eine Instanz des Effekts oder Audioprozessors, erstellen Sie eine Instanz von Effects mit den Audio- und Videoeffekten, die Sie auf die Mediendatei anwenden möchten, und fügen Sie das Effects-Objekt dann einem EditedMediaItem hinzu.

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

Benutzerdefinierte Effekte erstellen

Durch Erweitern der in Media3 enthaltenen Effekte können Sie benutzerdefinierte Effekte erstellen, die speziell auf Ihre Anwendungsfälle zugeschnitten sind. Im folgenden Beispiel wird die Unterklasse MatrixTransformation verwendet, um das Video in der ersten Sekunde der Wiedergabe so zu zoomen, dass es den Rahmen ausfüllt:

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

Wenn Sie das Verhalten eines Effekts weiter anpassen möchten, implementieren Sie ein GlShaderProgram. Die Methode queueInputFrame() wird verwendet, um Eingabe-Frames zu verarbeiten. Wenn Sie beispielsweise die Funktionen für maschinelles Lernen von MediaPipe nutzen möchten, können Sie einen MediaPipe-FrameProcessor verwenden, um jeden Frame durch ein MediaPipe-Diagramm zu senden. Ein Beispiel dafür finden Sie in der Transformer-Demo-App.

Vorschau der Effekte

Mit ExoPlayer können Sie sich eine Vorschau der Effekte ansehen, die einer Mediendatei hinzugefügt wurden, bevor Sie den Exportvorgang starten. Rufen Sie setVideoEffects() für Ihre ExoPlayer-Instanz auf und verwenden Sie dazu dasselbe Effects-Objekt wie für EditedMediaItem.

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

Sie können sich mit ExoPlayer auch eine Vorschau der Audioeffekte ansehen. Wenn Sie Ihre ExoPlayer-Instanz erstellen, übergeben Sie eine benutzerdefinierte RenderersFactory, die die Audio-Renderer des Players so konfiguriert, dass Audio an eine AudioSink ausgegeben wird, die Ihre AudioProcessor-Sequenz verwendet. Im folgenden Beispiel wird dies durch Überschreiben der Methode buildAudioSink() einer DefaultRenderersFactory erreicht.

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

Transformation starten

Erstellen Sie zum Schluss einen Transformer, um Ihre Änderungen zu übernehmen und die resultierende Mediendatei zu exportieren.

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

Sie können den Exportvorgang bei Bedarf auch mit Transformer.cancel() abbrechen.

Nach Fortschrittsupdates suchen

Transformer.start wird sofort zurückgegeben und asynchron ausgeführt. Wenn Sie den aktuellen Fortschritt einer Transformation abfragen möchten, rufen Sie Transformer.getProgress() auf. Diese Methode verwendet einen ProgressHolder. Wenn der Fortschrittsstatus verfügbar ist, d. h. wenn die Methode PROGRESS_STATE_AVAILABLE zurückgibt, wird der angegebene ProgressHolder mit dem aktuellen Fortschritt in Prozent aktualisiert.

Sie können auch einen Listener an Ihren Transformer anhängen, um über Abschluss- oder Fehlerereignisse benachrichtigt zu werden.