Tworzenie podstawowej aplikacji do edycji filmów za pomocą Media3 Transformer

Interfejsy Transformer API w Jetpack Media3 zostały zaprojektowane z myślą o wydajności i niezawodności edycji multimediów. Transformer obsługuje wiele operacji, takich jak:

  • Modyfikowanie filmu przez przycinanie, skalowanie i obracanie
  • dodawanie efektów, takich jak nakładki i filtry;
  • Przetwarzam specjalne formaty, takie jak HDR i filmy w zwolnionym tempie
  • Eksportowanie elementu multimedialnego po zastosowaniu zmian

Na tej stronie omawiamy niektóre kluczowe przypadki użycia danych w Transformerze. Więcej informacji znajdziesz w pełnych przewodnikach dotyczących Media3 Transformer.

Rozpocznij

Zacznij od dodania zależności do modułów Transformer, Effect i Common w Jetpack Media3:

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

Zastąp 1.3.1 preferowaną wersją biblioteki. Najnowszą wersję znajdziesz w informacjach o wersji.

Ważne zajęcia

Kategoria Cel
Transformer Uruchamiaj i zatrzymuj przekształcenia oraz sprawdzaj aktualizacje postępu ich realizacji.
EditedMediaItem Reprezentuje element multimedialny do przetworzenia oraz zmiany, które mają być do niego zastosowane.
Effects Kolekcja efektów audio i wideo.

Konfigurowanie danych wyjściowych

Za pomocą Transformer.Builder możesz teraz określić katalog videoMimeType i audioMimetype przez ustawienie funkcji bez konieczności tworzenia obiektu TransformationRequest.

Transkoduj między formatami

Poniższy kod pokazuje, jak skonfigurować obiekt Transformer, aby wyświetlać wideo w standardzie H.265/AVC i AAC:

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

Jeśli format nośnika wejściowego jest już zgodna z żądaniem przekształcenia dźwięku lub obrazu, Transformer automatycznie przełączy się na transmuksowanie, czyli skopiuje skompresowane próbki z kontenera wejściowego do kontenera wyjściowego bez modyfikacji. Pozwala to uniknąć kosztów obliczeniowych oraz potencjalnej utraty jakości dekodowania i ponownego kodowania w tym samym formacie.

Ustaw tryb HDR

Jeśli wejściowy plik multimedialny jest w formacie HDR, możesz wybrać spośród kilku różnych trybów przetwarzania informacji HDR przez Transformer. Prawdopodobnie wolisz użyć HDR_MODE_KEEP_HDR lub HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL.

HDR_MODE_KEEP_HDR HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL
Opis Zachowuje dane HDR, co oznacza, że format wyjściowy HDR jest taki sam jak format wejściowy HDR. Mapuj tonalne dane wejściowe HDR na SDR przy użyciu narzędzia do mapowania tonów OpenGL, co oznacza, że format wyjściowy będzie SDR.
Pomoc Obsługiwane przez interfejsy API na poziomie 31 i wyższych na urządzeniach z koderem obsługującym FEATURE_HdrEditing. Obsługiwane przez interfejsy API na poziomach 29 i nowszym.
Błędy Jeśli nie jest obsługiwany, spróbuje użyć zamiast niego HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL. Jeśli funkcja nie jest obsługiwana, zwraca ExportException.

Na urządzeniach obsługujących wymagane funkcje kodowania i Androida 13 (poziom interfejsu API 33) lub nowszego obiekty Transformer umożliwiają edytowanie filmów HDR. HDR_MODE_KEEP_HDR jest trybem domyślnym podczas tworzenia obiektu Composition, jak widać w tym kodzie:

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

Przygotowanie elementu multimedialnego

MediaItem reprezentuje element audio lub wideo w aplikacji. EditedMediaItem zbiera dane MediaItem wraz z przekształceniami, które mają do niego zastosowanie.

Przycinanie filmów

Aby usunąć niechciane fragmenty filmu, możesz ustawić niestandardową pozycję początkową i końcową, dodając ClippingConfiguration do elementu MediaItem.

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

Korzystanie z wbudowanych efektów

Media3 zawiera wiele wbudowanych efektów wideo służących do typowych przekształceń, takich jak:

Kategoria Efekt
Presentation Skaluj element multimedialny według rozdzielczości lub formatu obrazu
ScaleAndRotateTransformation Skaluj element multimedialny o mnożnik lub obróć go
Crop Przytnij element multimedialny do mniejszej lub większej ramki
OverlayEffect Dodaj na element multimedialny nakładkę tekstową lub graficzną

W przypadku efektów dźwiękowych możesz dodać sekwencję instancji AudioProcessor, które będą przekształcać nieprzetworzone dane dźwiękowe PCM. Możesz na przykład użyć ChannelMixingAudioProcessor, aby mieszać i skalować kanały audio.

Aby używać tych efektów, utwórz wystąpienie procesora efektu lub dźwięku, utwórz instancję Effects z efektami audio i wideo, które chcesz zastosować do elementu multimedialnego, a potem dodaj obiekt Effects do obiektu EditedMediaItem.

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

Tworzenie efektów niestandardowych

Poszerzając efekty dostępne w Media3, możesz tworzyć efekty niestandardowe dostosowane do Twoich potrzeb. W poniższym przykładzie użyj podklasy MatrixTransformation, aby powiększyć film tak, aby wypełniał klatkę podczas pierwszej sekundy odtwarzania:

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

Aby jeszcze bardziej dostosować działanie efektu, zaimplementuj GlShaderProgram. Do przetwarzania ramek wejściowych służy metoda queueInputFrame(). Aby na przykład wykorzystać możliwości systemów uczących się MediaPipe, możesz użyć elementu MediaPipe FrameProcessor do wysłania każdej klatki na wykres MediaPipe. Odpowiedni przykład znajdziesz w aplikacji demonstracyjnej Transformer.

Podgląd efektów

Za pomocą ExoPlayer możesz przed rozpoczęciem eksportowania wyświetlić podgląd efektów dodanych do elementu multimedialnego. Używając tego samego obiektu Effects co w przypadku EditedMediaItem, wywołaj setVideoEffects() w instancji ExoPlayer.

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

Możesz też wypróbować efekty dźwiękowe za pomocą ExoPlayer. Podczas tworzenia instancji ExoPlayer podaj niestandardowe RenderersFactory, które konfiguruje mechanizmy renderowania dźwięku w odtwarzaczu tak, aby odtwarzały dźwięk na urządzeniu AudioSink, który korzysta z Twojej sekwencji AudioProcessor. W poniższym przykładzie robimy to, zastępując metodę buildAudioSink() klasy 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();

Rozpocznij przekształcenie

Na koniec utwórz plik Transformer, aby zastosować zmiany i zacząć eksportować powstały w ten sposób element multimedialny.

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

W razie potrzeby możesz anulować proces eksportu w Transformer.cancel().

Sprawdź postęp

Transformer.start zwraca natychmiast i działa asynchronicznie. Aby przesłać zapytanie o bieżący postęp przekształcenia, wywołaj metodę Transformer.getProgress(). Ta metoda przyjmuje wartość ProgressHolder. Jeśli stan postępu jest dostępny, czyli jeśli metoda zwraca PROGRESS_STATE_AVAILABLE, podany ProgressHolder zostanie zaktualizowany o bieżącą wartość procentową postępu.

Możesz też dołączyć odbiornik do Transformer, aby otrzymywać powiadomienia o zdarzeniach zakończenia lub błędów.