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:

  • Videos durch Zuschneiden, Skalieren und Drehen bearbeiten
  • Effekte wie Overlays und Filter hinzufügen
  • Verarbeitung von Spezialformaten wie HDR und Zeitlupenvideo
  • Medienelement nach dem Übernehmen von Änderungen exportieren

Auf dieser Seite werden Sie durch einige der wichtigsten Anwendungsfälle für Transformer geführt. Weitere Informationen finden Sie in unseren vollständigen Leitfäden zu Media3 Transformer.

Jetzt starten

Fügen Sie zuerst eine Abhängigkeit für die Module „Transformer“, „Effect“ und „Common“ von Jetpack Media3 hinzu:

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

Ersetzen Sie 1.3.1 durch Ihre bevorzugte Version der Bibliothek. In den Versionshinweisen finden Sie die aktuelle Version.

Wichtige Kurse

Klasse Zweck
Transformer Transformationen starten und stoppen und Fortschrittsaktualisierungen für eine laufende Transformation prüfen.
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

Der folgende Code zeigt, wie ein Transformer-Objekt für die Ausgabe von H.265/AVC-Video und AAC-Audio konfiguriert wird:

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 der Transformationsanfrage für Audio oder Video entspricht, wechselt Transformer automatisch zu Transmuxing, d. h., die komprimierten Samples werden ohne Änderung vom Eingabecontainer in den Ausgabecontainer kopiert. Dadurch werden Rechenkosten und potenzielle Qualitätsverluste durch die Decodierung und Neucodierung im selben Format vermieden.

HDR-Modus festlegen

Wenn die Eingabemediendatei in einem HDR-Format vorliegt, können Sie zwischen verschiedenen Modi auswählen, mit denen Transformer die HDR-Informationen verarbeitet. 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 Behalten Sie die HDR-Daten bei, das heißt, das HDR-Ausgabeformat entspricht dem 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. Wird auf API-Ebenen ab 29 unterstützt.
Fehler Wenn die Methode nicht unterstützt wird, wird versucht, stattdessen HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL zu verwenden. Wenn sie nicht unterstützt wird, wird ein ExportException ausgelöst.

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

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 dem MediaItem-Element ClippingConfiguration 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. Beispiele:

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

Für Audioeffekte können Sie eine Sequenz von AudioProcessor-Instanzen hinzufügen, die die Rohaudiodaten (PCM) transformieren. Sie können beispielsweise ChannelMixingAudioProcessor verwenden, um Audiokanäle 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 das Medienelement anwenden möchten, und fügen Sie dann das Effects-Objekt zu 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

Wenn Sie die in Media3 enthaltenen Effekte erweitern, können Sie benutzerdefinierte Effekte erstellen, die auf Ihre Anwendungsfälle zugeschnitten sind. Verwenden Sie im folgenden Beispiel die abgeleitete Klasse MatrixTransformation, um das Video so zu zoomen, dass der Frame über die erste Sekunde der Wiedergabe ausgefüllt wird:

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 du das Verhalten eines Effekts weiter anpassen möchtest, kannst du eine GlShaderProgram implementieren. 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 hierzu finden Sie in der Transformer-Demo-App.

Effektvorschau ansehen

Mit ExoPlayer kannst du eine Vorschau der Effekte ansehen, die einem Medienelement hinzugefügt wurden, bevor du den Exportvorgang startest. Rufen Sie setVideoEffects() in der ExoPlayer-Instanz mit demselben Effects-Objekt wie für EditedMediaItem 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 können Sie Audioeffekte auch in der Vorschau 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 überschreiben wir die Methode buildAudioSink() einer 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();

Transformation starten

Erstellen Sie abschließend ein Transformer, um die Änderungen anzuwenden und mit dem Export des resultierenden Medienelements zu beginnen.

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

Auf ähnliche Weise können Sie den Exportvorgang bei Bedarf mit Transformer.cancel() abbrechen.

Nach Updates suchen

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 verwendet eine ProgressHolder. Wenn der Fortschrittsstatus verfügbar ist, d. h. 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.