Einfache Videobearbeitungsanwendung mit Media3 Transformer erstellen

Die Transformer APIs in Jetpack Media3 wurden entwickelt, um die Medienbearbeitung leistungsstark und zuverlässig zu gestalten. Transformer unterstützt eine Reihe von Vorgängen, darunter:

  • Video durch Zuschneiden, Skalieren und Drehen bearbeiten
  • Effekte wie Overlays und Filter hinzufügen
  • Verarbeitung von Sonderformaten wie HDR- und Zeitlupenvideos
  • Medienelement nach dem Bearbeiten exportieren

Auf dieser Seite werden einige der wichtigsten Anwendungsfälle für Transformer beschrieben. Weitere Informationen finden Sie in unseren vollständigen Leitfäden zum Media3-Transformer.

Erste Schritte

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

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

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

Wichtige Klassen

Klasse Zweck
Transformer Sie können Transformationen starten und beenden sowie den Fortschritt einer laufenden 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 das videoMimeType- und das audioMimetype-Verzeichnis festlegen, indem Sie die Funktion festlegen, ohne ein TransformationRequest-Objekt erstellen zu müssen.

Zwischen Formaten transcodieren

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

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 aus dem Eingabecontainer unverändert in den Ausgabecontainer kopiert. So werden die Rechenkosten und der potenzielle Qualitätsverlust durch das Decodieren und erneute Codieren im selben Format vermieden.

HDR-Modus festlegen

Wenn die Eingabemedien-Datei in einem HDR-Format vorliegt, können Sie zwischen verschiedenen Modi für die Verarbeitung der HDR-Informationen durch Transformer wählen. 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 per Tone Mapping in SDR umwandeln. Dazu wird ein OpenGL-Tone-Mapper verwendet. Das Ausgabeformat ist also SDR.
Support Wird auf API-Ebene 31 oder höher für Geräte unterstützt, die einen Encoder mit der Funktion FEATURE_HdrEditing enthalten. Wird ab API-Level 29 unterstützt.
Fehler Wenn nicht unterstützt, wird stattdessen versucht, HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL zu verwenden. Wenn nicht unterstützt, wird 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 zu sehen ist:

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 enthält ein MediaItem zusammen mit den Transformationen, die darauf angewendet werden sollen.

Video zuschneiden

Wenn Sie unerwünschte Teile eines Videos entfernen möchten, können Sie benutzerdefinierte Start- und Endpositionen festlegen, indem Sie der MediaItem ein 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 Medienelement nach Auflösung oder Seitenverhältnis skalieren
ScaleAndRotateTransformation Medienelement mit einem Multiplikator skalieren und/oder drehen
Crop Media-Element auf einen kleineren oder größeren Rahmen zuschneiden
OverlayEffect Text- oder Bild-Overlay über dem Medienelement hinzufügen

Für Audioeffekte können Sie eine Sequenz von AudioProcessor-Instanzen hinzufügen, die die PCM-Rohaudiodaten transformieren. Sie können beispielsweise 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 das Media-Element 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 die Erweiterung der in Media3 enthaltenen Effekte können Sie benutzerdefinierte Effekte erstellen, die 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 Frame 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 eine 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.

Effekte in der Vorschau ansehen

Mit ExoPlayer können Sie sich eine Vorschau der Effekte ansehen, die einem Media-Element hinzugefügt wurden, bevor Sie den Exportvorgang starten. Rufen Sie setVideoEffects() für Ihre ExoPlayer-Instanz auf und verwenden Sie dabei 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 Audioeffekte auch mit ExoPlayer in der Vorschau ansehen. Wenn Sie Ihre ExoPlayer-Instanz erstellen, übergeben Sie eine benutzerdefinierte RenderersFactory, mit der die Audio-Renderer des Players so konfiguriert werden, dass Audio an ein AudioSink ausgegeben wird, das Ihre AudioProcessor-Sequenz verwendet. Im folgenden Beispiel überschreiben wir dazu die Methode buildAudioSink() eines 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

Erstelle zum Schluss eine Transformer, um deine Änderungen zu übernehmen und das resultierende Media-Element 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.

Fortschrittsaktualisierungen prüfen

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

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