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

Interfejsy Transformer API w Jetpack Media3 zostały zaprojektowane tak, by zapewnić sprawną i niezawodną edycję multimediów. Transformer obsługuje wiele operacji, w tym:

  • Modyfikowanie filmu za pomocą przycinania, skalowania i obracania
  • dodawanie efektów, takich jak nakładki i filtry;
  • Przetwarzanie specjalnych formatów, takich jak HDR i filmy w zwolnionym tempie
  • Eksportowanie elementu multimedialnego po zastosowaniu zmian

Na tej stronie znajdziesz najważniejsze przypadki użycia omawiane przez Transformer. Więcej informacji znajdziesz w pełnych przewodnikach po narzędziu Media3 Transformer.

Rozpocznij

Aby rozpocząć, dodaj zależność 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 wybraną wersją biblioteki. Najnowszą wersję znajdziesz w informacjach o wersji.

Ważne zajęcia

Kategoria Cel
Transformer Rozpoczynaj i zatrzymuj przekształcenia oraz sprawdzaj aktualizacje trwającej przekształcenia.
EditedMediaItem Reprezentuje element multimedialny do przetworzenia i zmiany, które mają zostać do niego zastosowane.
Effects Kolekcja efektów audio i wideo.

Skonfiguruj dane wyjściowe

Za pomocą Transformer.Builder możesz teraz określić katalog videoMimeType i audioMimetype, ustawiając funkcję bez konieczności tworzenia obiektu TransformationRequest.

Transkoduj między formatami

Poniższy kod pokazuje, jak skonfigurować obiekt Transformer, aby zapewnić wyjście wideo H.265/AVC i dźwięk 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 pasuje już do żądania przekształcenia audio lub wideo, Transformer automatycznie przełącza się na transmuksowanie, czyli skopiuje skompresowane próbki z kontenera wejściowego do kontenera wyjściowego bez modyfikacji. Pozwala to uniknąć kosztów obliczeniowych i potencjalnej utraty jakości związanej z dekodowaniem i ponownym kodowaniem w tym samym formacie.

Ustaw tryb HDR

Jeśli wejściowy plik multimedialny jest w formacie HDR, możesz wybrać jeden z kilku trybów przetwarzania informacji HDR przez Transformer. Prawdopodobnie zechcesz 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 Zachowaj dane HDR, co oznacza, że format wyjściowy jest taki sam jak format wyjściowy HDR. Zmapowanie tonalnego obrazu HDR do SDR za pomocą mapera tonów OpenGL – oznacza to, że format wyjściowy będzie zapisany w SDR.
Pomoc Obsługiwane na poziomach API 31 i nowszych w przypadku urządzeń z koderem z obsługą FEATURE_HdrEditing. Obsługiwane na poziomach API 29 i nowszych.
Błędy Jeśli ta funkcja nie jest obsługiwana, spróbuj użyć interfejsu HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL. Jeśli nie jest obsługiwany, wysyła ExportException.

Na urządzeniach, które obsługują wymagane funkcje kodowania i mają Androida 13 (poziom interfejsu API 33) lub nowszego, obiekty Transformer umożliwiają edytowanie filmów HDR. HDR_MODE_KEEP_HDR to tryb domyślny podczas tworzenia obiektu Composition, jak pokazano 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();

Przygotowywanie elementu multimedialnego

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

Przycinanie filmu

Aby usunąć niechciane fragmenty filmu, możesz ustawić niestandardową pozycję początkową i końcową, dodając ClippingConfiguration do 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 przydatnych w przypadku 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óć element multimedialny
Crop Przytnij element multimedialny do mniejszej lub większej klatki
OverlayEffect Dodaj nakładkę z tekstem lub obrazem na element multimedialny.

W przypadku efektów audio możesz dodać sekwencję instancji AudioProcessor, która przekształci nieprzetworzone dane audio (PCM). Za pomocą ChannelMixingAudioProcessor możesz na przykład miksować i skalować kanały audio.

Aby użyć tych efektów, utwórz instancję efektu lub procesora audio, utwórz instancję Effects z efektami audio i wideo, które chcesz zastosować do elementu multimedialnego, a następnie 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

Rozszerzając efekty dostępne w Media3, możesz tworzyć efekty niestandardowe dostosowane do Twoich potrzeb. W tym przykładzie użyj podklasy MatrixTransformation, aby powiększyć film tak, by wypełnił 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. Metoda queueInputFrame() służy do przetwarzania ramek wejściowych. Aby na przykład wykorzystać możliwości systemów uczących się MediaPipe, możesz użyć MediaPipe FrameProcessor do wysłania każdej klatki za pomocą wykresu MediaPipe. Możesz zobaczyć to w aplikacji demonstracyjnej Transformer.

Podgląd efektów

Korzystając z ExoPlayer, możesz wyświetlić podgląd efektów dodanych do elementu multimedialnego przed rozpoczęciem procesu eksportowania. 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ż przetestować efekty dźwiękowe w narzędziu ExoPlayer. Podczas tworzenia instancji ExoPlayer przekaż niestandardowy obiekt RenderersFactory, który konfiguruje mechanizmy renderowania dźwięku w odtwarzaczu tak, aby wysyłały dźwięk do obiektu AudioSink, który używa sekwencji AudioProcessor. W poniższym przykładzie zastępujemy metodę buildAudioSink() w 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łcanie

Na koniec utwórz plik Transformer, aby zastosować zmiany, i zacznij eksportować wynikowy 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 podobny sposób w razie potrzeby możesz anulować proces eksportowania przy użyciu Transformer.cancel().

Sprawdzanie aktualnych informacji o postępach

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

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