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 ändern
- Effekte wie Overlays und Filter hinzufügen
- Verarbeitung spezieller Formate wie HDR- und Zeitlupenvideos
- Mediendatei nach dem Bearbeiten exportieren
Auf dieser Seite werden einige der wichtigsten Anwendungsfälle für Transformer beschrieben. Weitere Informationen findest du 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:
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"
Ersetzen Sie 1.11.1 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 Fortschrittsaktualisierungen für eine laufende Transformation prüfen. |
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 videoMimeType und audioMimetype direkt angeben, indem Sie die Funktion festlegen, ohne ein TransformationRequest-Objekt erstellen zu müssen.
Transcodierung zwischen Formaten
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 zum Transmuxing. Dabei werden die komprimierten Samples ohne Änderung aus dem Eingabecontainer 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 Eingabemediendatei im HDR-Format vorliegt, kannst du zwischen verschiedenen Modi wählen, wie Transformer die HDR-Informationen verarbeitet. Sie möchten wahrscheinlich 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, d. h., das HDR-Ausgabeformat entspricht dem HDR-Eingabeformat. | HDR-Eingabe per Tone Mapping mit einem OpenGL-Tone-Mapper in SDR umwandeln, d. h. das Ausgabeformat ist SDR. |
| Support | Unterstützt auf API-Level 31 oder höher für Geräte mit einem Encoder mit der Funktion FEATURE_HdrEditing. |
Unterstützt ab API-Level 29. |
| 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 Android 13 (API-Level 33) oder höher ausführen, 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 ein Audio- oder Videoelement in Ihrer App dar. Ein EditedMediaItem erfasst ein MediaItem zusammen mit den darauf anzuwendenden Transformationen.
Video schneiden
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 | Effekt |
|---|---|
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
Wenn Sie die in Media3 enthaltenen Effekte erweitern, können Sie benutzerdefinierte Effekte für Ihre Anwendungsfälle erstellen. 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 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.
Effekte in der Vorschau ansehen
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 dieselben Effect-Objekte wie für EditedMediaItem.
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();
Mit ExoPlayer können Sie sich auch eine Vorschau von Audioeffekten ansehen. Übergib beim Erstellen der ExoPlayer-Instanz eine benutzerdefinierte RenderersFactory, die die Audiorenderer des Players so konfiguriert, dass Audio an eine AudioSink ausgegeben wird, die deine AudioProcessor-Sequenz verwendet. Im folgenden Beispiel wird dazu die Methode buildAudioSink() eines DefaultRenderersFactory überschrieben.
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. 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 angegebene 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.