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

Interfejsy Transformer API w Jetpack Media3 zostały zaprojektowane do edycji multimediów wydajną i niezawodną. 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 omawiamy wybrane kluczowe przypadki użycia Transformer. Więcej informacji znajdziesz w pełnych przewodnikach: Media3 Transformer.

Rozpocznij

Aby rozpocząć, dodaj zależność do modułów Transformer, Efekt i Powszechne Jetpack Media3:

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

Zastąp 1.4.1 preferowaną wersją bibliotece. Możesz zapoznać się z informacje o wersji aby zobaczyć najnowszą wersję.

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

Korzystając z Transformer.Builder, możesz teraz określić videoMimeType i audioMimetype przez ustawienie funkcji bez konieczności tworzenia TransformationRequest obiekt.

Transkoduj między formatami

Ten kod pokazuje, jak skonfigurować obiekt Transformer wyjście wideo H.265/AVC i dźwięk w formacie 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ż zgodny z żądaniem przekształcenia dźwięku lub wideo, Transformer automatycznie przełącza się na transmuksowanie, czyli kopiowanie skompresowanych próbek z kontenera wejściowego do kontenera wyjściowego bez modyfikacji. Pozwala to uniknąć kosztów obliczeniowych i potencjalnej utraty jakości na dekodowaniu i ponownym kodowaniu w tym samym formacie.

Ustaw tryb HDR

Jeśli wejściowy plik multimedialny jest w formacie HDR, możesz wybrać jedną z tych opcji: w różnych trybach przetwarzania informacji HDR przez Transformer. Prawdopodobnie chcesz użyć funkcji 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 wyższy, obiekty Transformer umożliwiają edytowanie filmów HDR. Domyślnym trybem podczas tworzenia obiektu Composition jest HDR_MODE_KEEP_HDR. jak 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 oznacza plik audio. lub element wideo w aplikacji. EditedMediaItem zbiera: MediaItem z przekształceniami, które mają do niego zastosowanie.

Przycinanie filmu

Aby usunąć niechciane fragmenty filmu, możesz ustawić niestandardowy początek i koniec filmu pozycji, 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 przy typowych przekształceniach, np.:

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.

Do efektów dźwiękowych możesz dodać sekwencję AudioProcessor które przekształcą nieprzetworzone dane dźwiękowe (PCM). Na przykład możesz użyć ChannelMixingAudioProcessor do miksowania i skalowania kanałów audio.

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

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 do swoich potrzeb. W poniższym przykładzie użyj podklasy MatrixTransformation, aby powiększyć film i zapełnić klatkę nad pierwszą sekunda 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 funkcję GlShaderProgram Metoda queueInputFrame() jest używana do przetwarzania ramek wejściowych. Aby na przykład: wykorzystują systemy uczące się MediaPipe, możesz użyć parametru MediaPipe FrameProcessor; aby wysłać każdą klatkę za pomocą grafu MediaPipe. Zobacz przykład w Aplikacja demonstracyjna Transformer.

Podgląd efektów

ExoPlayer pozwala podglądać efekty dodany do elementu multimedialnego przed rozpoczęciem procesu eksportowania. Używanie tego samego Effects obiekt jak w przypadku EditedMediaItem, wywołaj setVideoEffects() na 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 ExoPlayer, przekaż niestandardowe źródło RenderersFactory, które konfiguruje mechanizm renderowania dźwięku w odtwarzaczu do wersji AudioSink, która używa Twojego Sekwencja AudioProcessor. W przykładzie poniżej robimy to, zastępując Metoda buildAudioSink() dla 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ć utworzony 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 w podobny sposób anulować proces eksportowania, używając Transformer.cancel()

Sprawdzanie aktualnych informacji o postępach

Funkcja Transformer.start wraca natychmiast i działa asynchronicznie. Aby wysłać zapytanie obecny postęp przekształcenia, wywołanie Transformer.getProgress() Ta metoda wymaga ProgressHolder, a jeśli stan postępu jest dostępny, oznacza to, że jeśli metoda zwraca PROGRESS_STATE_AVAILABLE, podana wartość Aplikacja ProgressHolder zostanie zaktualizowana o aktualny odsetek postępu.

Możesz też dołączyć listener do: Transformer, aby otrzymywać powiadomienia o zdarzeniach zakończenia lub błędach.