Создайте базовое приложение для редактирования видео с помощью Media3 Transformer.

API-интерфейсы Transformer в Jetpack Media3 предназначены для повышения производительности и надежности редактирования мультимедиа. Transformer поддерживает ряд операций, в том числе:

  • Изменение видео с помощью обрезки, масштабирования и вращения.
  • Добавление эффектов, таких как наложения и фильтры
  • Обработка специальных форматов, таких как HDR и замедленное видео.
  • Экспорт медиа-элемента после применения изменений

На этой странице представлены некоторые ключевые варианты использования Transformer. Для получения более подробной информации вы можете обратиться к нашим полным руководствам по Media3 Transformer .

Начать

Для начала добавьте зависимость к модулям Transformer, Effect и Common 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"

Обязательно замените 1.4.1 предпочитаемой версией библиотеки. Вы можете обратиться к примечаниям к выпуску , чтобы увидеть последнюю версию.

Важные занятия

Сорт Цель
Transformer Запускайте и останавливайте преобразования, а также проверяйте обновления хода выполнения текущего преобразования.
EditedMediaItem Представляет элемент мультимедиа для обработки и изменения, которые необходимо к нему применить.
Effects Коллекция аудио и видео эффектов.

Настройте вывод

С помощью Transformer.Builder теперь вы можете указать каталог videoMimeType и audioMimetype , задав функцию без необходимости создания объекта TransformationRequest .

Перекодирование между форматами

Следующий код показывает, как настроить объект Transformer для вывода видео H.265/AVC и аудио AAC:

Котлин

val transformer = Transformer.Builder(context)
    .setVideoMimeType(MimeTypes.VIDEO_H265)
    .setAudioMimeType(MimeTypes.AUDIO_AAC)
    .build()

Ява

Transformer transformer = new Transformer.Builder(context)
    .setVideoMimeType(MimeTypes.VIDEO_H265)
    .setAudioMimeType(MimeTypes.AUDIO_AAC)
    .build();

Если формат входного мультимедиа уже соответствует запросу на преобразование аудио или видео, Transformer автоматически переключается на трансмуксирование , то есть копирование сжатых сэмплов из входного контейнера в выходной контейнер без изменений. Это позволяет избежать вычислительных затрат и потенциальной потери качества при декодировании и перекодировании в одном и том же формате.

Установить режим HDR

Если входной медиафайл имеет формат HDR, вы можете выбрать один из нескольких различных режимов обработки информации HDR Transformer. Вероятно, вы захотите использовать HDR_MODE_KEEP_HDR или HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL .

HDR_MODE_KEEP_HDR HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL
Описание Сохраняйте данные HDR, то есть выходной формат HDR совпадает с входным форматом HDR. Ввод Tonemap HDR в SDR с использованием преобразователя тонов OpenGL, что означает, что выходной формат будет в SDR.
Поддерживать Поддерживается на уровнях API 31+ для устройств, которые включают кодировщик с возможностью FEATURE_HdrEditing . Поддерживается на уровнях API 29+.
Ошибки Если не поддерживается, вместо этого пытается использовать HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL . Если не поддерживается, выдает ExportException .

На устройствах, которые поддерживают необходимые возможности кодирования и работают под управлением Android 13 (уровень API 33) или более поздней версии, объекты Transformer позволяют редактировать HDR-видео. HDR_MODE_KEEP_HDR — это режим по умолчанию при построении объекта Composition , как показано в следующем коде:

Котлин

val composition = Composition.Builder(
    ImmutableList.of(videoSequence))
    .setHdrMode(HDR_MODE_KEEP_HDR)
    .build()

Ява

Composition composition = new Composition.Builder(
    ImmutableList.of(videoSequence))
    .setHdrMode(Composition.HDR_MODE_KEEP_HDR)
    .build();

Подготовьте медиа-материал

MediaItem представляет собой аудио- или видеоэлемент в вашем приложении. EditedMediaItem собирает MediaItem вместе с преобразованиями, которые необходимо к нему применить.

Обрезать видео

Чтобы удалить ненужные части видео, вы можете установить собственные начальную и конечную позиции, добавив ClippingConfiguration в MediaItem .

Котлин

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

Ява

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

Используйте встроенные эффекты

Media3 включает в себя ряд встроенных видеоэффектов для распространенных преобразований, например:

Сорт Эффект
Presentation Масштабируйте медиа-элемент по разрешению или соотношению сторон.
ScaleAndRotateTransformation Масштабируйте элемент мультимедиа с помощью множителя и/или вращайте элемент мультимедиа.
Crop Обрезать медиа-элемент до меньшего или большего размера кадра
OverlayEffect Добавьте наложение текста или изображения поверх элемента мультимедиа.

Для звуковых эффектов вы можете добавить последовательность экземпляров AudioProcessor , которые будут преобразовывать необработанные аудиоданные (PCM). Например, вы можете использовать ChannelMixingAudioProcessor для микширования и масштабирования аудиоканалов.

Чтобы использовать эти эффекты, создайте экземпляр эффекта или аудиопроцессора, создайте экземпляр Effects с аудио- и видеоэффектами, которые вы хотите применить к элементу мультимедиа, а затем добавьте объект Effects в EditedMediaItem .

Котлин

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

Ява

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

Создавайте собственные эффекты

Расширяя эффекты, включенные в Media3, вы можете создавать собственные эффекты, соответствующие вашим сценариям использования. В следующем примере используйте подкласс MatrixTransformation , чтобы увеличить масштаб видео до заполнения кадра в течение первой секунды воспроизведения:

Котлин

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

Ява

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

Чтобы дополнительно настроить поведение эффекта, реализуйте GlShaderProgram . queueInputFrame() используется для обработки входных кадров. Например, чтобы использовать возможности машинного обучения MediaPipe , вы можете использовать MediaPipe FrameProcessor для отправки каждого кадра через граф MediaPipe. См. пример этого в демонстрационном приложении Transformer .

Предварительный просмотр эффектов

С помощью ExoPlayer вы можете просмотреть эффекты, добавленные к элементу мультимедиа, перед началом процесса экспорта. Используя тот же объект Effects , что и для EditedMediaItem , вызовите setVideoEffects() в своем экземпляре ExoPlayer.

Котлин

val player = ExoPlayer.builder(context)
    .build()
    .also { exoPlayer ->
        exoPlayer.setMediaItem(inputMediaItem)
        exoPlayer.setVideoEffects(effects)
        exoPlayer.prepare()
    }

Ява

ExoPlayer player = new ExoPlayer.builder(context).build();
player.setMediaItem(inputMediaItem);
player.setVideoEffects(effects);
exoPlayer.prepare();

Вы также можете предварительно просмотреть аудиоэффекты с помощью ExoPlayer. При создании экземпляра ExoPlayer передайте пользовательский RenderersFactory , который настраивает средства рендеринга звука проигрывателя для вывода звука в AudioSink , который использует вашу последовательность AudioProcessor . В приведенном ниже примере мы делаем это, переопределив метод buildAudioSink() DefaultRenderersFactory .

Котлин

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

Ява

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

Начать трансформацию

Наконец, создайте Transformer , чтобы применить ваши изменения и начать экспорт полученного медиа-элемента.

Котлин

val transformer = Transformer.Builder(context)
    .addListener(listener)
    .build()
transformer.start(editedMediaItem, outputPath)

Ява

Transformer transformer = new Transformer.Builder(context)
    .addListener(listener)
    .build();
transformer.start(editedMediaItem, outputPath);

При необходимости вы также можете отменить процесс экспорта с помощью Transformer.cancel() .

Следите за обновлениями прогресса

Transformer.start немедленно возвращается и запускается асинхронно. Чтобы запросить текущий ход преобразования, вызовите Transformer.getProgress() . Этот метод принимает ProgressHolder , и если состояние прогресса доступно, то есть если метод возвращает PROGRESS_STATE_AVAILABLE , то предоставленный ProgressHolder будет обновлен с учетом текущего процента прогресса.

Вы также можете подключить прослушиватель к своему Transformer , чтобы получать уведомления о событиях завершения или ошибки.