Einfache Videobearbeitungsanwendung mit Media3 Transformer erstellen

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

  • Videos zuschneiden, skalieren und drehen
  • Effekte wie Overlays und Filter hinzufügen
  • Verarbeitung spezieller Formate wie HDR und Zeitlupenvideos
  • Medienelement nach dem Übernehmen von Änderungen exportieren

Auf dieser Seite werden einige der wichtigsten Anwendungsfälle von Transformer erläutert. Weitere Informationen findest du in unseren vollständigen Leitfäden zu Media3 Transformer.

Erste Schritte

Füge als Erstes eine Abhängigkeit von den Transformer-, Effect- und Common-Modulen von Jetpack Media3 hinzu:

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

Ersetzen Sie 1.4.1 durch die gewünschte Version der Bibliothek. Informationen zur neuesten Version finden Sie in den Versionshinweisen.

Wichtige Klassen

Klasse Zweck
Transformer Sie können Transformationen starten und beenden und sich den Fortschritt einer laufenden Transformation ansehen.
EditedMediaItem Stellt ein zu verarbeitendes Medienelement und die darauf anzuwendenden Änderungen dar.
Effects Eine Sammlung von Audio- und Videoeffekten.

Ausgabe konfigurieren

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

Zwischen Formaten transcodieren

Im folgenden Code wird gezeigt, wie du ein Transformer-Objekt für die Ausgabe von H.265/AVC-Video und AAC-Audio konfigurierst:

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, d. h., die komprimierten Samples werden ohne Änderung aus dem Eingabecontainer in den Ausgabecontainer kopiert. So werden die Rechenkosten und potenziellen Qualitätsverluste durch Decodieren und erneutes Codieren im selben Format vermieden.

HDR-Modus festlegen

Wenn die Eingabemediendatei im HDR-Format vorliegt, kannst du aus mehreren Modi für die Verarbeitung der HDR-Informationen durch Transformer auswählen. Sie möchten 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 HDR-Daten beibehalten, d. h. das HDR-Ausgabeformat ist dasselbe wie das HDR-Eingabeformat. Tonemap-HDR-Eingabe für SDR mit einem OpenGL Tonemapper, d. h. das Ausgabeformat ist SDR.
Support Wird auf API-Levels 31 und höher für Geräte mit einem Encoder mit FEATURE_HdrEditing-Funktion unterstützt. Unterstützt ab API-Level 29.
Fehler Wenn nicht unterstützt, wird stattdessen versucht, HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL zu verwenden. Wenn die Option nicht unterstützt wird, wird ExportException ausgegeben.

Auf Geräten, die die erforderlichen Codierungsfunktionen unterstützen und auf denen Android 13 (API-Level 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 dargestellt:

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

Medienelement vorbereiten

Ein MediaItem steht für ein Audio- oder Videoelement in Ihrer App. Ein EditedMediaItem erfasst ein MediaItem zusammen mit den Transformationen, die darauf angewendet werden sollen.

Video zuschneiden

Wenn du unerwünschte Teile aus einem Video entfernen möchtest, kannst du benutzerdefinierte Start- und Endpositionen festlegen, indem du ein ClippingConfiguration zum MediaItem hinzufügst.

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 bietet eine Reihe von integrierten Videoeffekten für gängige Transformationen, z. B.:

Klasse Effekte
Presentation Medienelement nach Auflösung oder Seitenverhältnis skalieren
ScaleAndRotateTransformation Medienelement um einen Multiplikator skalieren und/oder drehen
Crop Medienelement auf einen kleineren oder größeren Frame zuschneiden
OverlayEffect Füge dem Medienelement ein Text- oder Bild-Overlay hinzu.

Für Audioeffekte kannst du eine Sequenz von AudioProcessor-Instanzen hinzufügen, die die Roh-Audiodaten (PCM) transformieren. So kannst du beispielsweise mit einem ChannelMixingAudioProcessor Audiokanäle mischen und skalieren.

Wenn du diese Effekte verwenden möchtest, musst du eine Instanz des Effekts oder Audioprozessors erstellen, eine Instanz von Effects mit den Audio- und Videoeffekten erstellen, die du auf das Medienelement anwenden möchtest, und das Effects-Objekt dann einem EditedMediaItem hinzufügen.

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

Wenn du die in Media3 enthaltenen Effekte erweiterst, kannst du benutzerdefinierte Effekte für deine Anwendungsfälle erstellen. Im folgenden Beispiel wird die Unterklasse MatrixTransformation verwendet, um das Video so zu zoomen, dass es den Frame in der ersten Wiedergabesekunde fü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 eine GlShaderProgram. Die Methode queueInputFrame() wird zum Verarbeiten von Eingabeframes verwendet. Zur Nutzung der ML-Funktionen von MediaPipe können Sie beispielsweise mit MediaPipe FrameProcessor jeden Frame über ein MediaPipe-Diagramm senden. Ein Beispiel dafür finden Sie in der Transformer-Demo-App.

Effekte in der Vorschau ansehen

Mit ExoPlayer kannst du dir eine Vorschau der Effekte ansehen, die einem Medienelement hinzugefügt wurden, bevor du mit dem Export beginnst. Rufe mit demselben Effects-Objekt wie für die EditedMediaItem die setVideoEffects()-Methode auf deiner ExoPlayer-Instanz auf.

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

Mit ExoPlayer kannst du dir auch eine Vorschau von Audioeffekten ansehen. Übergeben Sie beim Erstellen der Instanz ExoPlayer eine benutzerdefinierte RenderersFactory, die die Audio-Renderer des Players so konfiguriert, dass sie Audiodaten an eine AudioSink ausgeben, die Ihre AudioProcessor-Sequenz verwendet. Im folgenden Beispiel wird dies dadurch erreicht, dass die Methode buildAudioSink() eines DefaultRenderersFactory überschrieben wird.

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

Erstelle abschließend eine Transformer, um deine Änderungen anzuwenden und das resultierende Medienelement 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.

Auf Fortschrittsaktualisierungen prüfen

Transformer.start wird sofort zurückgegeben und wird asynchron ausgeführt. Rufen Sie Transformer.getProgress() auf, um den aktuellen Fortschritt einer Transformation abzufragen. Diese Methode nimmt eine ProgressHolder an. Wenn der Fortschrittsstatus verfügbar ist, d. h. wenn die Methode PROGRESS_STATE_AVAILABLE zurückgibt, wird die angegebene ProgressHolder mit dem aktuellen Fortschrittsprozentsatz aktualisiert.

Sie können auch einen Listener an Transformer anhängen, um bei Abschluss- oder Fehlerereignissen benachrichtigt zu werden.